home *** CD-ROM | disk | FTP | other *** search
/ ftp.editplus.com / 2015-02-07.ftp.editplus.com.tar / ftp.editplus.com / epp370p0943_0925.exe / [0] / emmet_epp.js < prev    next >
Text File  |  2014-04-30  |  386KB  |  13,271 lines

  1. //     Underscore.js 1.3.3
  2. //     (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
  3. //     Underscore is freely distributable under the MIT license.
  4. //     Portions of Underscore are inspired or borrowed from Prototype,
  5. //     Oliver Steele's Functional, and John Resig's Micro-Templating.
  6. //     For all details and documentation:
  7. //     http://documentcloud.github.com/underscore
  8.  
  9. var _ = (function() {
  10.  
  11.   // Baseline setup
  12.   // --------------
  13.  
  14.   // Establish the root object, `window` in the browser, or `global` on the server.
  15.   var root = this;
  16.  
  17.   // Save the previous value of the `_` variable.
  18.   var previousUnderscore = root._;
  19.  
  20.   // Establish the object that gets returned to break out of a loop iteration.
  21.   var breaker = {};
  22.  
  23.   // Save bytes in the minified (but not gzipped) version:
  24.   var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
  25.  
  26.   // Create quick reference variables for speed access to core prototypes.
  27.   var slice            = ArrayProto.slice,
  28.       unshift          = ArrayProto.unshift,
  29.       toString         = ObjProto.toString,
  30.       hasOwnProperty   = ObjProto.hasOwnProperty;
  31.  
  32.   // All **ECMAScript 5** native function implementations that we hope to use
  33.   // are declared here.
  34.   var
  35.     nativeForEach      = ArrayProto.forEach,
  36.     nativeMap          = ArrayProto.map,
  37.     nativeReduce       = ArrayProto.reduce,
  38.     nativeReduceRight  = ArrayProto.reduceRight,
  39.     nativeFilter       = ArrayProto.filter,
  40.     nativeEvery        = ArrayProto.every,
  41.     nativeSome         = ArrayProto.some,
  42.     nativeIndexOf      = ArrayProto.indexOf,
  43.     nativeLastIndexOf  = ArrayProto.lastIndexOf,
  44.     nativeIsArray      = Array.isArray,
  45.     nativeKeys         = Object.keys,
  46.     nativeBind         = FuncProto.bind;
  47.  
  48.   // Create a safe reference to the Underscore object for use below.
  49.   var _ = function(obj) { return new wrapper(obj); };
  50.  
  51.   // Export the Underscore object for **Node.js**, with
  52.   // backwards-compatibility for the old `require()` API. If we're in
  53.   // the browser, add `_` as a global object via a string identifier,
  54.   // for Closure Compiler "advanced" mode.
  55.   if (typeof exports !== 'undefined') {
  56.     if (typeof module !== 'undefined' && module.exports) {
  57.       exports = module.exports = _;
  58.     }
  59.     exports._ = _;
  60.   } else {
  61.     root['_'] = _;
  62.   }
  63.  
  64.   // Current version.
  65.   _.VERSION = '1.3.3';
  66.  
  67.   // Collection Functions
  68.   // --------------------
  69.  
  70.   // The cornerstone, an `each` implementation, aka `forEach`.
  71.   // Handles objects with the built-in `forEach`, arrays, and raw objects.
  72.   // Delegates to **ECMAScript 5**'s native `forEach` if available.
  73.   var each = _.each = _.forEach = function(obj, iterator, context) {
  74.     if (obj == null) return;
  75.     if (nativeForEach && obj.forEach === nativeForEach) {
  76.       obj.forEach(iterator, context);
  77.     } else if (obj.length === +obj.length) {
  78.       for (var i = 0, l = obj.length; i < l; i++) {
  79.         if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
  80.       }
  81.     } else {
  82.       for (var key in obj) {
  83.         if (_.has(obj, key)) {
  84.           if (iterator.call(context, obj[key], key, obj) === breaker) return;
  85.         }
  86.       }
  87.     }
  88.   };
  89.  
  90.   // Return the results of applying the iterator to each element.
  91.   // Delegates to **ECMAScript 5**'s native `map` if available.
  92.   _.map = _.collect = function(obj, iterator, context) {
  93.     var results = [];
  94.     if (obj == null) return results;
  95.     if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
  96.     each(obj, function(value, index, list) {
  97.       results[results.length] = iterator.call(context, value, index, list);
  98.     });
  99.     if (obj.length === +obj.length) results.length = obj.length;
  100.     return results;
  101.   };
  102.  
  103.   // **Reduce** builds up a single result from a list of values, aka `inject`,
  104.   // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
  105.   _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
  106.     var initial = arguments.length > 2;
  107.     if (obj == null) obj = [];
  108.     if (nativeReduce && obj.reduce === nativeReduce) {
  109.       if (context) iterator = _.bind(iterator, context);
  110.       return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
  111.     }
  112.     each(obj, function(value, index, list) {
  113.       if (!initial) {
  114.         memo = value;
  115.         initial = true;
  116.       } else {
  117.         memo = iterator.call(context, memo, value, index, list);
  118.       }
  119.     });
  120.     if (!initial) throw new TypeError('Reduce of empty array with no initial value');
  121.     return memo;
  122.   };
  123.  
  124.   // The right-associative version of reduce, also known as `foldr`.
  125.   // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
  126.   _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
  127.     var initial = arguments.length > 2;
  128.     if (obj == null) obj = [];
  129.     if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
  130.       if (context) iterator = _.bind(iterator, context);
  131.       return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
  132.     }
  133.     var reversed = _.toArray(obj).reverse();
  134.     if (context && !initial) iterator = _.bind(iterator, context);
  135.     return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
  136.   };
  137.  
  138.   // Return the first value which passes a truth test. Aliased as `detect`.
  139.   _.find = _.detect = function(obj, iterator, context) {
  140.     var result;
  141.     any(obj, function(value, index, list) {
  142.       if (iterator.call(context, value, index, list)) {
  143.         result = value;
  144.         return true;
  145.       }
  146.     });
  147.     return result;
  148.   };
  149.  
  150.   // Return all the elements that pass a truth test.
  151.   // Delegates to **ECMAScript 5**'s native `filter` if available.
  152.   // Aliased as `select`.
  153.   _.filter = _.select = function(obj, iterator, context) {
  154.     var results = [];
  155.     if (obj == null) return results;
  156.     if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
  157.     each(obj, function(value, index, list) {
  158.       if (iterator.call(context, value, index, list)) results[results.length] = value;
  159.     });
  160.     return results;
  161.   };
  162.  
  163.   // Return all the elements for which a truth test fails.
  164.   _.reject = function(obj, iterator, context) {
  165.     var results = [];
  166.     if (obj == null) return results;
  167.     each(obj, function(value, index, list) {
  168.       if (!iterator.call(context, value, index, list)) results[results.length] = value;
  169.     });
  170.     return results;
  171.   };
  172.  
  173.   // Determine whether all of the elements match a truth test.
  174.   // Delegates to **ECMAScript 5**'s native `every` if available.
  175.   // Aliased as `all`.
  176.   _.every = _.all = function(obj, iterator, context) {
  177.     var result = true;
  178.     if (obj == null) return result;
  179.     if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
  180.     each(obj, function(value, index, list) {
  181.       if (!(result = result && iterator.call(context, value, index, list))) return breaker;
  182.     });
  183.     return !!result;
  184.   };
  185.  
  186.   // Determine if at least one element in the object matches a truth test.
  187.   // Delegates to **ECMAScript 5**'s native `some` if available.
  188.   // Aliased as `any`.
  189.   var any = _.some = _.any = function(obj, iterator, context) {
  190.     iterator || (iterator = _.identity);
  191.     var result = false;
  192.     if (obj == null) return result;
  193.     if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
  194.     each(obj, function(value, index, list) {
  195.       if (result || (result = iterator.call(context, value, index, list))) return breaker;
  196.     });
  197.     return !!result;
  198.   };
  199.  
  200.   // Determine if a given value is included in the array or object using `===`.
  201.   // Aliased as `contains`.
  202.   _.include = _.contains = function(obj, target) {
  203.     var found = false;
  204.     if (obj == null) return found;
  205.     if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
  206.     found = any(obj, function(value) {
  207.       return value === target;
  208.     });
  209.     return found;
  210.   };
  211.  
  212.   // Invoke a method (with arguments) on every item in a collection.
  213.   _.invoke = function(obj, method) {
  214.     var args = slice.call(arguments, 2);
  215.     return _.map(obj, function(value) {
  216.       return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
  217.     });
  218.   };
  219.  
  220.   // Convenience version of a common use case of `map`: fetching a property.
  221.   _.pluck = function(obj, key) {
  222.     return _.map(obj, function(value){ return value[key]; });
  223.   };
  224.  
  225.   // Return the maximum element or (element-based computation).
  226.   _.max = function(obj, iterator, context) {
  227.     if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
  228.     if (!iterator && _.isEmpty(obj)) return -Infinity;
  229.     var result = {computed : -Infinity};
  230.     each(obj, function(value, index, list) {
  231.       var computed = iterator ? iterator.call(context, value, index, list) : value;
  232.       computed >= result.computed && (result = {value : value, computed : computed});
  233.     });
  234.     return result.value;
  235.   };
  236.  
  237.   // Return the minimum element (or element-based computation).
  238.   _.min = function(obj, iterator, context) {
  239.     if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
  240.     if (!iterator && _.isEmpty(obj)) return Infinity;
  241.     var result = {computed : Infinity};
  242.     each(obj, function(value, index, list) {
  243.       var computed = iterator ? iterator.call(context, value, index, list) : value;
  244.       computed < result.computed && (result = {value : value, computed : computed});
  245.     });
  246.     return result.value;
  247.   };
  248.  
  249.   // Shuffle an array.
  250.   _.shuffle = function(obj) {
  251.     var shuffled = [], rand;
  252.     each(obj, function(value, index, list) {
  253.       rand = Math.floor(Math.random() * (index + 1));
  254.       shuffled[index] = shuffled[rand];
  255.       shuffled[rand] = value;
  256.     });
  257.     return shuffled;
  258.   };
  259.  
  260.   // Sort the object's values by a criterion produced by an iterator.
  261.   _.sortBy = function(obj, val, context) {
  262.     var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
  263.     return _.pluck(_.map(obj, function(value, index, list) {
  264.       return {
  265.         value : value,
  266.         criteria : iterator.call(context, value, index, list)
  267.       };
  268.     }).sort(function(left, right) {
  269.       var a = left.criteria, b = right.criteria;
  270.       if (a === void 0) return 1;
  271.       if (b === void 0) return -1;
  272.       return a < b ? -1 : a > b ? 1 : 0;
  273.     }), 'value');
  274.   };
  275.  
  276.   // Groups the object's values by a criterion. Pass either a string attribute
  277.   // to group by, or a function that returns the criterion.
  278.   _.groupBy = function(obj, val) {
  279.     var result = {};
  280.     var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
  281.     each(obj, function(value, index) {
  282.       var key = iterator(value, index);
  283.       (result[key] || (result[key] = [])).push(value);
  284.     });
  285.     return result;
  286.   };
  287.  
  288.   // Use a comparator function to figure out at what index an object should
  289.   // be inserted so as to maintain order. Uses binary search.
  290.   _.sortedIndex = function(array, obj, iterator) {
  291.     iterator || (iterator = _.identity);
  292.     var low = 0, high = array.length;
  293.     while (low < high) {
  294.       var mid = (low + high) >> 1;
  295.       iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
  296.     }
  297.     return low;
  298.   };
  299.  
  300.   // Safely convert anything iterable into a real, live array.
  301.   _.toArray = function(obj) {
  302.     if (!obj)                                     return [];
  303.     if (_.isArray(obj))                           return slice.call(obj);
  304.     if (_.isArguments(obj))                       return slice.call(obj);
  305.     if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
  306.     return _.values(obj);
  307.   };
  308.  
  309.   // Return the number of elements in an object.
  310.   _.size = function(obj) {
  311.     return _.isArray(obj) ? obj.length : _.keys(obj).length;
  312.   };
  313.  
  314.   // Array Functions
  315.   // ---------------
  316.  
  317.   // Get the first element of an array. Passing **n** will return the first N
  318.   // values in the array. Aliased as `head` and `take`. The **guard** check
  319.   // allows it to work with `_.map`.
  320.   _.first = _.head = _.take = function(array, n, guard) {
  321.     return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
  322.   };
  323.  
  324.   // Returns everything but the last entry of the array. Especcialy useful on
  325.   // the arguments object. Passing **n** will return all the values in
  326.   // the array, excluding the last N. The **guard** check allows it to work with
  327.   // `_.map`.
  328.   _.initial = function(array, n, guard) {
  329.     return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
  330.   };
  331.  
  332.   // Get the last element of an array. Passing **n** will return the last N
  333.   // values in the array. The **guard** check allows it to work with `_.map`.
  334.   _.last = function(array, n, guard) {
  335.     if ((n != null) && !guard) {
  336.       return slice.call(array, Math.max(array.length - n, 0));
  337.     } else {
  338.       return array[array.length - 1];
  339.     }
  340.   };
  341.  
  342.   // Returns everything but the first entry of the array. Aliased as `tail`.
  343.   // Especially useful on the arguments object. Passing an **index** will return
  344.   // the rest of the values in the array from that index onward. The **guard**
  345.   // check allows it to work with `_.map`.
  346.   _.rest = _.tail = function(array, index, guard) {
  347.     return slice.call(array, (index == null) || guard ? 1 : index);
  348.   };
  349.  
  350.   // Trim out all falsy values from an array.
  351.   _.compact = function(array) {
  352.     return _.filter(array, function(value){ return !!value; });
  353.   };
  354.  
  355.   // Return a completely flattened version of an array.
  356.   _.flatten = function(array, shallow) {
  357.     return _.reduce(array, function(memo, value) {
  358.       if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
  359.       memo[memo.length] = value;
  360.       return memo;
  361.     }, []);
  362.   };
  363.  
  364.   // Return a version of the array that does not contain the specified value(s).
  365.   _.without = function(array) {
  366.     return _.difference(array, slice.call(arguments, 1));
  367.   };
  368.  
  369.   // Produce a duplicate-free version of the array. If the array has already
  370.   // been sorted, you have the option of using a faster algorithm.
  371.   // Aliased as `unique`.
  372.   _.uniq = _.unique = function(array, isSorted, iterator) {
  373.     var initial = iterator ? _.map(array, iterator) : array;
  374.     var results = [];
  375.     // The `isSorted` flag is irrelevant if the array only contains two elements.
  376.     if (array.length < 3) isSorted = true;
  377.     _.reduce(initial, function (memo, value, index) {
  378.       if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
  379.         memo.push(value);
  380.         results.push(array[index]);
  381.       }
  382.       return memo;
  383.     }, []);
  384.     return results;
  385.   };
  386.  
  387.   // Produce an array that contains the union: each distinct element from all of
  388.   // the passed-in arrays.
  389.   _.union = function() {
  390.     return _.uniq(_.flatten(arguments, true));
  391.   };
  392.  
  393.   // Produce an array that contains every item shared between all the
  394.   // passed-in arrays. (Aliased as "intersect" for back-compat.)
  395.   _.intersection = _.intersect = function(array) {
  396.     var rest = slice.call(arguments, 1);
  397.     return _.filter(_.uniq(array), function(item) {
  398.       return _.every(rest, function(other) {
  399.         return _.indexOf(other, item) >= 0;
  400.       });
  401.     });
  402.   };
  403.  
  404.   // Take the difference between one array and a number of other arrays.
  405.   // Only the elements present in just the first array will remain.
  406.   _.difference = function(array) {
  407.     var rest = _.flatten(slice.call(arguments, 1), true);
  408.     return _.filter(array, function(value){ return !_.include(rest, value); });
  409.   };
  410.  
  411.   // Zip together multiple lists into a single array -- elements that share
  412.   // an index go together.
  413.   _.zip = function() {
  414.     var args = slice.call(arguments);
  415.     var length = _.max(_.pluck(args, 'length'));
  416.     var results = new Array(length);
  417.     for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
  418.     return results;
  419.   };
  420.  
  421.   // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
  422.   // we need this function. Return the position of the first occurrence of an
  423.   // item in an array, or -1 if the item is not included in the array.
  424.   // Delegates to **ECMAScript 5**'s native `indexOf` if available.
  425.   // If the array is large and already in sort order, pass `true`
  426.   // for **isSorted** to use binary search.
  427.   _.indexOf = function(array, item, isSorted) {
  428.     if (array == null) return -1;
  429.     var i, l;
  430.     if (isSorted) {
  431.       i = _.sortedIndex(array, item);
  432.       return array[i] === item ? i : -1;
  433.     }
  434.     if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
  435.     for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
  436.     return -1;
  437.   };
  438.  
  439.   // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
  440.   _.lastIndexOf = function(array, item) {
  441.     if (array == null) return -1;
  442.     if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
  443.     var i = array.length;
  444.     while (i--) if (i in array && array[i] === item) return i;
  445.     return -1;
  446.   };
  447.  
  448.   // Generate an integer Array containing an arithmetic progression. A port of
  449.   // the native Python `range()` function. See
  450.   // [the Python documentation](http://docs.python.org/library/functions.html#range).
  451.   _.range = function(start, stop, step) {
  452.     if (arguments.length <= 1) {
  453.       stop = start || 0;
  454.       start = 0;
  455.     }
  456.     step = arguments[2] || 1;
  457.  
  458.     var len = Math.max(Math.ceil((stop - start) / step), 0);
  459.     var idx = 0;
  460.     var range = new Array(len);
  461.  
  462.     while(idx < len) {
  463.       range[idx++] = start;
  464.       start += step;
  465.     }
  466.  
  467.     return range;
  468.   };
  469.  
  470.   // Function (ahem) Functions
  471.   // ------------------
  472.  
  473.   // Reusable constructor function for prototype setting.
  474.   var ctor = function(){};
  475.  
  476.   // Create a function bound to a given object (assigning `this`, and arguments,
  477.   // optionally). Binding with arguments is also known as `curry`.
  478.   // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
  479.   // We check for `func.bind` first, to fail fast when `func` is undefined.
  480.   _.bind = function bind(func, context) {
  481.     var bound, args;
  482.     if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
  483.     if (!_.isFunction(func)) throw new TypeError;
  484.     args = slice.call(arguments, 2);
  485.     return bound = function() {
  486.       if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
  487.       ctor.prototype = func.prototype;
  488.       var self = new ctor;
  489.       var result = func.apply(self, args.concat(slice.call(arguments)));
  490.       if (Object(result) === result) return result;
  491.       return self;
  492.     };
  493.   };
  494.  
  495.   // Bind all of an object's methods to that object. Useful for ensuring that
  496.   // all callbacks defined on an object belong to it.
  497.   _.bindAll = function(obj) {
  498.     var funcs = slice.call(arguments, 1);
  499.     if (funcs.length == 0) funcs = _.functions(obj);
  500.     each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
  501.     return obj;
  502.   };
  503.  
  504.   // Memoize an expensive function by storing its results.
  505.   _.memoize = function(func, hasher) {
  506.     var memo = {};
  507.     hasher || (hasher = _.identity);
  508.     return function() {
  509.       var key = hasher.apply(this, arguments);
  510.       return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
  511.     };
  512.   };
  513.  
  514.   // Delays a function for the given number of milliseconds, and then calls
  515.   // it with the arguments supplied.
  516.   _.delay = function(func, wait) {
  517.     var args = slice.call(arguments, 2);
  518.     return setTimeout(function(){ return func.apply(null, args); }, wait);
  519.   };
  520.  
  521.   // Defers a function, scheduling it to run after the current call stack has
  522.   // cleared.
  523.   _.defer = function(func) {
  524.     return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
  525.   };
  526.  
  527.   // Returns a function, that, when invoked, will only be triggered at most once
  528.   // during a given window of time.
  529.   _.throttle = function(func, wait) {
  530.     var context, args, timeout, throttling, more, result;
  531.     var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
  532.     return function() {
  533.       context = this; args = arguments;
  534.       var later = function() {
  535.         timeout = null;
  536.         if (more) func.apply(context, args);
  537.         whenDone();
  538.       };
  539.       if (!timeout) timeout = setTimeout(later, wait);
  540.       if (throttling) {
  541.         more = true;
  542.       } else {
  543.         result = func.apply(context, args);
  544.       }
  545.       whenDone();
  546.       throttling = true;
  547.       return result;
  548.     };
  549.   };
  550.  
  551.   // Returns a function, that, as long as it continues to be invoked, will not
  552.   // be triggered. The function will be called after it stops being called for
  553.   // N milliseconds. If `immediate` is passed, trigger the function on the
  554.   // leading edge, instead of the trailing.
  555.   _.debounce = function(func, wait, immediate) {
  556.     var timeout;
  557.     return function() {
  558.       var context = this, args = arguments;
  559.       var later = function() {
  560.         timeout = null;
  561.         if (!immediate) func.apply(context, args);
  562.       };
  563.       if (immediate && !timeout) func.apply(context, args);
  564.       clearTimeout(timeout);
  565.       timeout = setTimeout(later, wait);
  566.     };
  567.   };
  568.  
  569.   // Returns a function that will be executed at most one time, no matter how
  570.   // often you call it. Useful for lazy initialization.
  571.   _.once = function(func) {
  572.     var ran = false, memo;
  573.     return function() {
  574.       if (ran) return memo;
  575.       ran = true;
  576.       return memo = func.apply(this, arguments);
  577.     };
  578.   };
  579.  
  580.   // Returns the first function passed as an argument to the second,
  581.   // allowing you to adjust arguments, run code before and after, and
  582.   // conditionally execute the original function.
  583.   _.wrap = function(func, wrapper) {
  584.     return function() {
  585.       var args = [func].concat(slice.call(arguments, 0));
  586.       return wrapper.apply(this, args);
  587.     };
  588.   };
  589.  
  590.   // Returns a function that is the composition of a list of functions, each
  591.   // consuming the return value of the function that follows.
  592.   _.compose = function() {
  593.     var funcs = arguments;
  594.     return function() {
  595.       var args = arguments;
  596.       for (var i = funcs.length - 1; i >= 0; i--) {
  597.         args = [funcs[i].apply(this, args)];
  598.       }
  599.       return args[0];
  600.     };
  601.   };
  602.  
  603.   // Returns a function that will only be executed after being called N times.
  604.   _.after = function(times, func) {
  605.     if (times <= 0) return func();
  606.     return function() {
  607.       if (--times < 1) { return func.apply(this, arguments); }
  608.     };
  609.   };
  610.  
  611.   // Object Functions
  612.   // ----------------
  613.  
  614.   // Retrieve the names of an object's properties.
  615.   // Delegates to **ECMAScript 5**'s native `Object.keys`
  616.   _.keys = nativeKeys || function(obj) {
  617.     if (obj !== Object(obj)) throw new TypeError('Invalid object');
  618.     var keys = [];
  619.     for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
  620.     return keys;
  621.   };
  622.  
  623.   // Retrieve the values of an object's properties.
  624.   _.values = function(obj) {
  625.     return _.map(obj, _.identity);
  626.   };
  627.  
  628.   // Return a sorted list of the function names available on the object.
  629.   // Aliased as `methods`
  630.   _.functions = _.methods = function(obj) {
  631.     var names = [];
  632.     for (var key in obj) {
  633.       if (_.isFunction(obj[key])) names.push(key);
  634.     }
  635.     return names.sort();
  636.   };
  637.  
  638.   // Extend a given object with all the properties in passed-in object(s).
  639.   _.extend = function(obj) {
  640.     each(slice.call(arguments, 1), function(source) {
  641.       for (var prop in source) {
  642.         obj[prop] = source[prop];
  643.       }
  644.     });
  645.     return obj;
  646.   };
  647.  
  648.   // Return a copy of the object only containing the whitelisted properties.
  649.   _.pick = function(obj) {
  650.     var result = {};
  651.     each(_.flatten(slice.call(arguments, 1)), function(key) {
  652.       if (key in obj) result[key] = obj[key];
  653.     });
  654.     return result;
  655.   };
  656.  
  657.   // Fill in a given object with default properties.
  658.   _.defaults = function(obj) {
  659.     each(slice.call(arguments, 1), function(source) {
  660.       for (var prop in source) {
  661.         if (obj[prop] == null) obj[prop] = source[prop];
  662.       }
  663.     });
  664.     return obj;
  665.   };
  666.  
  667.   // Create a (shallow-cloned) duplicate of an object.
  668.   _.clone = function(obj) {
  669.     if (!_.isObject(obj)) return obj;
  670.     return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  671.   };
  672.  
  673.   // Invokes interceptor with the obj, and then returns obj.
  674.   // The primary purpose of this method is to "tap into" a method chain, in
  675.   // order to perform operations on intermediate results within the chain.
  676.   _.tap = function(obj, interceptor) {
  677.     interceptor(obj);
  678.     return obj;
  679.   };
  680.  
  681.   // Internal recursive comparison function.
  682.   function eq(a, b, stack) {
  683.     // Identical objects are equal. `0 === -0`, but they aren't identical.
  684.     // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
  685.     if (a === b) return a !== 0 || 1 / a == 1 / b;
  686.     // A strict comparison is necessary because `null == undefined`.
  687.     if (a == null || b == null) return a === b;
  688.     // Unwrap any wrapped objects.
  689.     if (a._chain) a = a._wrapped;
  690.     if (b._chain) b = b._wrapped;
  691.     // Invoke a custom `isEqual` method if one is provided.
  692.     if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
  693.     if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
  694.     // Compare `[[Class]]` names.
  695.     var className = toString.call(a);
  696.     if (className != toString.call(b)) return false;
  697.     switch (className) {
  698.       // Strings, numbers, dates, and booleans are compared by value.
  699.       case '[object String]':
  700.         // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
  701.         // equivalent to `new String("5")`.
  702.         return a == String(b);
  703.       case '[object Number]':
  704.         // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
  705.         // other numeric values.
  706.         return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
  707.       case '[object Date]':
  708.       case '[object Boolean]':
  709.         // Coerce dates and booleans to numeric primitive values. Dates are compared by their
  710.         // millisecond representations. Note that invalid dates with millisecond representations
  711.         // of `NaN` are not equivalent.
  712.         return +a == +b;
  713.       // RegExps are compared by their source patterns and flags.
  714.       case '[object RegExp]':
  715.         return a.source == b.source &&
  716.                a.global == b.global &&
  717.                a.multiline == b.multiline &&
  718.                a.ignoreCase == b.ignoreCase;
  719.     }
  720.     if (typeof a != 'object' || typeof b != 'object') return false;
  721.     // Assume equality for cyclic structures. The algorithm for detecting cyclic
  722.     // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
  723.     var length = stack.length;
  724.     while (length--) {
  725.       // Linear search. Performance is inversely proportional to the number of
  726.       // unique nested structures.
  727.       if (stack[length] == a) return true;
  728.     }
  729.     // Add the first object to the stack of traversed objects.
  730.     stack.push(a);
  731.     var size = 0, result = true;
  732.     // Recursively compare objects and arrays.
  733.     if (className == '[object Array]') {
  734.       // Compare array lengths to determine if a deep comparison is necessary.
  735.       size = a.length;
  736.       result = size == b.length;
  737.       if (result) {
  738.         // Deep compare the contents, ignoring non-numeric properties.
  739.         while (size--) {
  740.           // Ensure commutative equality for sparse arrays.
  741.           if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
  742.         }
  743.       }
  744.     } else {
  745.       // Objects with different constructors are not equivalent.
  746.       if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
  747.       // Deep compare objects.
  748.       for (var key in a) {
  749.         if (_.has(a, key)) {
  750.           // Count the expected number of properties.
  751.           size++;
  752.           // Deep compare each member.
  753.           if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
  754.         }
  755.       }
  756.       // Ensure that both objects contain the same number of properties.
  757.       if (result) {
  758.         for (key in b) {
  759.           if (_.has(b, key) && !(size--)) break;
  760.         }
  761.         result = !size;
  762.       }
  763.     }
  764.     // Remove the first object from the stack of traversed objects.
  765.     stack.pop();
  766.     return result;
  767.   }
  768.  
  769.   // Perform a deep comparison to check if two objects are equal.
  770.   _.isEqual = function(a, b) {
  771.     return eq(a, b, []);
  772.   };
  773.  
  774.   // Is a given array, string, or object empty?
  775.   // An "empty" object has no enumerable own-properties.
  776.   _.isEmpty = function(obj) {
  777.     if (obj == null) return true;
  778.     if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
  779.     for (var key in obj) if (_.has(obj, key)) return false;
  780.     return true;
  781.   };
  782.  
  783.   // Is a given value a DOM element?
  784.   _.isElement = function(obj) {
  785.     return !!(obj && obj.nodeType == 1);
  786.   };
  787.  
  788.   // Is a given value an array?
  789.   // Delegates to ECMA5's native Array.isArray
  790.   _.isArray = nativeIsArray || function(obj) {
  791.     return toString.call(obj) == '[object Array]';
  792.   };
  793.  
  794.   // Is a given variable an object?
  795.   _.isObject = function(obj) {
  796.     return obj === Object(obj);
  797.   };
  798.  
  799.   // Is a given variable an arguments object?
  800.   _.isArguments = function(obj) {
  801.     return toString.call(obj) == '[object Arguments]';
  802.   };
  803.   if (!_.isArguments(arguments)) {
  804.     _.isArguments = function(obj) {
  805.       return !!(obj && _.has(obj, 'callee'));
  806.     };
  807.   }
  808.  
  809.   // Is a given value a function?
  810.   _.isFunction = function(obj) {
  811.     return toString.call(obj) == '[object Function]';
  812.   };
  813.  
  814.   // Is a given value a string?
  815.   _.isString = function(obj) {
  816.     return toString.call(obj) == '[object String]';
  817.   };
  818.  
  819.   // Is a given value a number?
  820.   _.isNumber = function(obj) {
  821.     return toString.call(obj) == '[object Number]';
  822.   };
  823.  
  824.   // Is a given object a finite number?
  825.   _.isFinite = function(obj) {
  826.     return _.isNumber(obj) && isFinite(obj);
  827.   };
  828.  
  829.   // Is the given value `NaN`?
  830.   _.isNaN = function(obj) {
  831.     // `NaN` is the only value for which `===` is not reflexive.
  832.     return obj !== obj;
  833.   };
  834.  
  835.   // Is a given value a boolean?
  836.   _.isBoolean = function(obj) {
  837.     return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
  838.   };
  839.  
  840.   // Is a given value a date?
  841.   _.isDate = function(obj) {
  842.     return toString.call(obj) == '[object Date]';
  843.   };
  844.  
  845.   // Is the given value a regular expression?
  846.   _.isRegExp = function(obj) {
  847.     return toString.call(obj) == '[object RegExp]';
  848.   };
  849.  
  850.   // Is a given value equal to null?
  851.   _.isNull = function(obj) {
  852.     return obj === null;
  853.   };
  854.  
  855.   // Is a given variable undefined?
  856.   _.isUndefined = function(obj) {
  857.     return obj === void 0;
  858.   };
  859.  
  860.   // Has own property?
  861.   _.has = function(obj, key) {
  862.     return hasOwnProperty.call(obj, key);
  863.   };
  864.  
  865.   // Utility Functions
  866.   // -----------------
  867.  
  868.   // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  869.   // previous owner. Returns a reference to the Underscore object.
  870.   _.noConflict = function() {
  871.     root._ = previousUnderscore;
  872.     return this;
  873.   };
  874.  
  875.   // Keep the identity function around for default iterators.
  876.   _.identity = function(value) {
  877.     return value;
  878.   };
  879.  
  880.   // Run a function **n** times.
  881.   _.times = function (n, iterator, context) {
  882.     for (var i = 0; i < n; i++) iterator.call(context, i);
  883.   };
  884.  
  885.   // Escape a string for HTML interpolation.
  886.   _.escape = function(string) {
  887.     return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
  888.   };
  889.  
  890.   // If the value of the named property is a function then invoke it;
  891.   // otherwise, return it.
  892.   _.result = function(object, property) {
  893.     if (object == null) return null;
  894.     var value = object[property];
  895.     return _.isFunction(value) ? value.call(object) : value;
  896.   };
  897.  
  898.   // Add your own custom functions to the Underscore object, ensuring that
  899.   // they're correctly added to the OOP wrapper as well.
  900.   _.mixin = function(obj) {
  901.     each(_.functions(obj), function(name){
  902.       addToWrapper(name, _[name] = obj[name]);
  903.     });
  904.   };
  905.  
  906.   // Generate a unique integer id (unique within the entire client session).
  907.   // Useful for temporary DOM ids.
  908.   var idCounter = 0;
  909.   _.uniqueId = function(prefix) {
  910.     var id = idCounter++;
  911.     return prefix ? prefix + id : id;
  912.   };
  913.  
  914.   // By default, Underscore uses ERB-style template delimiters, change the
  915.   // following template settings to use alternative delimiters.
  916.   _.templateSettings = {
  917.     evaluate    : /<%([\s\S]+?)%>/g,
  918.     interpolate : /<%=([\s\S]+?)%>/g,
  919.     escape      : /<%-([\s\S]+?)%>/g
  920.   };
  921.  
  922.   // When customizing `templateSettings`, if you don't want to define an
  923.   // interpolation, evaluation or escaping regex, we need one that is
  924.   // guaranteed not to match.
  925.   var noMatch = /.^/;
  926.  
  927.   // Certain characters need to be escaped so that they can be put into a
  928.   // string literal.
  929.   var escapes = {
  930.     '\\': '\\',
  931.     "'": "'",
  932.     'r': '\r',
  933.     'n': '\n',
  934.     't': '\t',
  935.     'u2028': '\u2028',
  936.     'u2029': '\u2029'
  937.   };
  938.  
  939.   for (var p in escapes) escapes[escapes[p]] = p;
  940.   var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
  941.   var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
  942.  
  943.   // Within an interpolation, evaluation, or escaping, remove HTML escaping
  944.   // that had been previously added.
  945.   var unescape = function(code) {
  946.     return code.replace(unescaper, function(match, escape) {
  947.       return escapes[escape];
  948.     });
  949.   };
  950.  
  951.   // JavaScript micro-templating, similar to John Resig's implementation.
  952.   // Underscore templating handles arbitrary delimiters, preserves whitespace,
  953.   // and correctly escapes quotes within interpolated code.
  954.   _.template = function(text, data, settings) {
  955.     settings = _.defaults(settings || {}, _.templateSettings);
  956.  
  957.     // Compile the template source, taking care to escape characters that
  958.     // cannot be included in a string literal and then unescape them in code
  959.     // blocks.
  960.     var source = "__p+='" + text
  961.       .replace(escaper, function(match) {
  962.         return '\\' + escapes[match];
  963.       })
  964.       .replace(settings.escape || noMatch, function(match, code) {
  965.         return "'+\n_.escape(" + unescape(code) + ")+\n'";
  966.       })
  967.       .replace(settings.interpolate || noMatch, function(match, code) {
  968.         return "'+\n(" + unescape(code) + ")+\n'";
  969.       })
  970.       .replace(settings.evaluate || noMatch, function(match, code) {
  971.         return "';\n" + unescape(code) + "\n;__p+='";
  972.       }) + "';\n";
  973.  
  974.     // If a variable is not specified, place data values in local scope.
  975.     if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
  976.  
  977.     source = "var __p='';" +
  978.       "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
  979.       source + "return __p;\n";
  980.  
  981.     var render = new Function(settings.variable || 'obj', '_', source);
  982.     if (data) return render(data, _);
  983.     var template = function(data) {
  984.       return render.call(this, data, _);
  985.     };
  986.  
  987.     // Provide the compiled function source as a convenience for build time
  988.     // precompilation.
  989.     template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
  990.       source + '}';
  991.  
  992.     return template;
  993.   };
  994.  
  995.   // Add a "chain" function, which will delegate to the wrapper.
  996.   _.chain = function(obj) {
  997.     return _(obj).chain();
  998.   };
  999.  
  1000.   // The OOP Wrapper
  1001.   // ---------------
  1002.  
  1003.   // If Underscore is called as a function, it returns a wrapped object that
  1004.   // can be used OO-style. This wrapper holds altered versions of all the
  1005.   // underscore functions. Wrapped objects may be chained.
  1006.   var wrapper = function(obj) { this._wrapped = obj; };
  1007.  
  1008.   // Expose `wrapper.prototype` as `_.prototype`
  1009.   _.prototype = wrapper.prototype;
  1010.  
  1011.   // Helper function to continue chaining intermediate results.
  1012.   var result = function(obj, chain) {
  1013.     return chain ? _(obj).chain() : obj;
  1014.   };
  1015.  
  1016.   // A method to easily add functions to the OOP wrapper.
  1017.   var addToWrapper = function(name, func) {
  1018.     wrapper.prototype[name] = function() {
  1019.       var args = slice.call(arguments);
  1020.       unshift.call(args, this._wrapped);
  1021.       return result(func.apply(_, args), this._chain);
  1022.     };
  1023.   };
  1024.  
  1025.   // Add all of the Underscore functions to the wrapper object.
  1026.   _.mixin(_);
  1027.  
  1028.   // Add all mutator Array functions to the wrapper.
  1029.   each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
  1030.     var method = ArrayProto[name];
  1031.     wrapper.prototype[name] = function() {
  1032.       var wrapped = this._wrapped;
  1033.       method.apply(wrapped, arguments);
  1034.       var length = wrapped.length;
  1035.       if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
  1036.       return result(wrapped, this._chain);
  1037.     };
  1038.   });
  1039.  
  1040.   // Add all accessor Array functions to the wrapper.
  1041.   each(['concat', 'join', 'slice'], function(name) {
  1042.     var method = ArrayProto[name];
  1043.     wrapper.prototype[name] = function() {
  1044.       return result(method.apply(this._wrapped, arguments), this._chain);
  1045.     };
  1046.   });
  1047.  
  1048.   // Start chaining a wrapped Underscore object.
  1049.   wrapper.prototype.chain = function() {
  1050.     this._chain = true;
  1051.     return this;
  1052.   };
  1053.  
  1054.   // Extracts the result from a wrapped and chained object.
  1055.   wrapper.prototype.value = function() {
  1056.     return this._wrapped;
  1057.   };
  1058.   return _;
  1059. }).call({});
  1060. /**
  1061.  * Core Emmet object, available in global scope
  1062.  */
  1063. var emmet = (function(global) {
  1064.     var defaultSyntax = 'html';
  1065.     var defaultProfile = 'plain';
  1066.     
  1067.     if (typeof _ == 'undefined') {
  1068.         try {
  1069.             // avoid collisions with RequireJS loader
  1070.             // also, JS obfuscators tends to translate
  1071.             // a["name"] to a.name, which also breaks RequireJS
  1072.             _ = global[['require'][0]]('underscore'); // node.js
  1073.         } catch (e) {}
  1074.     }
  1075.  
  1076.     if (typeof _ == 'undefined') {
  1077.         throw 'Cannot access to Underscore.js lib';
  1078.     }
  1079.  
  1080.     /** List of registered modules */
  1081.     var modules = {
  1082.         _ : _
  1083.     };
  1084.     
  1085.     /**
  1086.      * Shared empty constructor function to aid in prototype-chain creation.
  1087.      */
  1088.     var ctor = function(){};
  1089.     
  1090.     /**
  1091.      * Helper function to correctly set up the prototype chain, for subclasses.
  1092.      * Similar to `goog.inherits`, but uses a hash of prototype properties and
  1093.      * class properties to be extended.
  1094.      * Took it from Backbone.
  1095.      * @param {Object} parent
  1096.      * @param {Object} protoProps
  1097.      * @param {Object} staticProps
  1098.      * @returns {Object}
  1099.      */
  1100.     function inherits(parent, protoProps, staticProps) {
  1101.         var child;
  1102.  
  1103.         // The constructor function for the new subclass is either defined by
  1104.         // you (the "constructor" property in your `extend` definition), or
  1105.         // defaulted by us to simply call the parent's constructor.
  1106.         if (protoProps && protoProps.hasOwnProperty('constructor')) {
  1107.             child = protoProps.constructor;
  1108.         } else {
  1109.             child = function() {
  1110.                 parent.apply(this, arguments);
  1111.             };
  1112.         }
  1113.  
  1114.         // Inherit class (static) properties from parent.
  1115.         _.extend(child, parent);
  1116.  
  1117.         // Set the prototype chain to inherit from `parent`, without calling
  1118.         // `parent`'s constructor function.
  1119.         ctor.prototype = parent.prototype;
  1120.         child.prototype = new ctor();
  1121.  
  1122.         // Add prototype properties (instance properties) to the subclass,
  1123.         // if supplied.
  1124.         if (protoProps)
  1125.             _.extend(child.prototype, protoProps);
  1126.  
  1127.         // Add static properties to the constructor function, if supplied.
  1128.         if (staticProps)
  1129.             _.extend(child, staticProps);
  1130.  
  1131.         // Correctly set child's `prototype.constructor`.
  1132.         child.prototype.constructor = child;
  1133.  
  1134.         // Set a convenience property in case the parent's prototype is needed
  1135.         // later.
  1136.         child.__super__ = parent.prototype;
  1137.  
  1138.         return child;
  1139.     };
  1140.     
  1141.     /**
  1142.      * @type Function Function that loads module definition if it's not defined
  1143.      */
  1144.     var moduleLoader = null;
  1145.     
  1146.     /**
  1147.      * Generic Emmet module loader (actually, it doesnΓÇÖt load anything, just 
  1148.      * returns module reference). Not using `require` name to avoid conflicts
  1149.      * with Node.js and RequireJS
  1150.      */
  1151.     function r(name) {
  1152.         if (!(name in modules) && moduleLoader)
  1153.             moduleLoader(name);
  1154.         
  1155.         return modules[name];
  1156.     }
  1157.     
  1158.     return {
  1159.         /**
  1160.          * Simple, AMD-like module definition. The module will be added into
  1161.          * <code>emmet</code> object and will be available via
  1162.          * <code>emmet.require(name)</code> or <code>emmet[name]</code>
  1163.          * @param {String} name
  1164.          * @param {Function} factory
  1165.          * @memberOf emmet
  1166.          */
  1167.         define: function(name, factory) {
  1168.             // do not let redefine existing properties
  1169.             if (!(name in modules)) {
  1170.                 modules[name] = _.isFunction(factory) 
  1171.                     ? this.exec(factory)
  1172.                     : factory;
  1173.             }
  1174.         },
  1175.         
  1176.         /**
  1177.          * Returns reference to Emmet module
  1178.          * @param {String} name Module name
  1179.          */
  1180.         require: r,
  1181.         
  1182.         /**
  1183.          * Helper method that just executes passed function but with all 
  1184.          * important arguments like 'require' and '_'
  1185.          * @param {Function} fn
  1186.          * @param {Object} context Execution context
  1187.          */
  1188.         exec: function(fn, context) {
  1189.             return fn.call(context || global, _.bind(r, this), _, this);
  1190.         },
  1191.         
  1192.         /**
  1193.          * The self-propagating extend function for classes.
  1194.          * Took it from Backbone 
  1195.          * @param {Object} protoProps
  1196.          * @param {Object} classProps
  1197.          * @returns {Object}
  1198.          */
  1199.         extend: function(protoProps, classProps) {
  1200.             var child = inherits(this, protoProps, classProps);
  1201.             child.extend = this.extend;
  1202.             // a hack required to WSH inherit `toString` method
  1203.             if (protoProps.hasOwnProperty('toString'))
  1204.                 child.prototype.toString = protoProps.toString;
  1205.             return child;
  1206.         },
  1207.         
  1208.         /**
  1209.          * The essential function that expands Emmet abbreviation
  1210.          * @param {String} abbr Abbreviation to parse
  1211.          * @param {String} syntax Abbreviation's context syntax
  1212.          * @param {String} profile Output profile (or its name)
  1213.          * @param {Object} contextNode Contextual node where abbreviation is
  1214.          * written
  1215.          * @return {String}
  1216.          */
  1217.         expandAbbreviation: function(abbr, syntax, profile, contextNode) {
  1218.             if (!abbr) return '';
  1219.             
  1220.             syntax = syntax || defaultSyntax;
  1221. //            profile = profile || defaultProfile;
  1222.             
  1223.             var filters = r('filters');
  1224.             var parser = r('abbreviationParser');
  1225.             
  1226.             profile = r('profile').get(profile, syntax);
  1227.             r('tabStops').resetTabstopIndex();
  1228.             
  1229.             var data = filters.extractFromAbbreviation(abbr);
  1230.             var outputTree = parser.parse(data[0], {
  1231.                 syntax: syntax, 
  1232.                 contextNode: contextNode
  1233.             });
  1234.             
  1235.             var filtersList = filters.composeList(syntax, profile, data[1]);
  1236.             filters.apply(outputTree, filtersList, profile);
  1237.             return outputTree.toString();
  1238.         },
  1239.         
  1240.         /**
  1241.          * Returns default syntax name used in abbreviation engine
  1242.          * @returns {String}
  1243.          */
  1244.         defaultSyntax: function() {
  1245.             return defaultSyntax;
  1246.         },
  1247.         
  1248.         /**
  1249.          * Returns default profile name used in abbreviation engine
  1250.          * @returns {String}
  1251.          */
  1252.         defaultProfile: function() {
  1253.             return defaultProfile;
  1254.         },
  1255.         
  1256.         /**
  1257.          * Log message into console if it exists
  1258.          */
  1259.         log: function() {
  1260.             if (global.console && global.console.log)
  1261.                 global.console.log.apply(global.console, arguments);
  1262.         },
  1263.         
  1264.         /**
  1265.          * Setups function that should synchronously load undefined modules
  1266.          * @param {Function} fn
  1267.          */
  1268.         setModuleLoader: function(fn) {
  1269.             moduleLoader = fn;
  1270.         }
  1271.     };
  1272. })(this);
  1273.  
  1274. // export core for Node.JS
  1275. if (typeof exports !== 'undefined') {
  1276.     if (typeof module !== 'undefined' && module.exports) {
  1277.         exports = module.exports = emmet;
  1278.     }
  1279.     exports.emmet = emmet;
  1280. }
  1281.  
  1282. // export as Require.js module
  1283. if (typeof define !== 'undefined') {
  1284.     define(emmet);
  1285. }/**
  1286.  * Emmet abbreviation parser.
  1287.  * Takes string abbreviation and recursively parses it into a tree. The parsed 
  1288.  * tree can be transformed into a string representation with 
  1289.  * <code>toString()</code> method. Note that string representation is defined
  1290.  * by custom processors (called <i>filters</i>), not by abbreviation parser 
  1291.  * itself.
  1292.  * 
  1293.  * This module can be extended with custom pre-/post-processors to shape-up
  1294.  * final tree or its representation. Actually, many features of abbreviation 
  1295.  * engine are defined in other modules as tree processors
  1296.  * 
  1297.  * 
  1298.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  1299.  * @link http://chikuyonok.ru
  1300.  * @memberOf __abbreviationParser
  1301.  * @constructor
  1302.  * @param {Function} require
  1303.  * @param {Underscore} _
  1304.  */
  1305. emmet.define('abbreviationParser', function(require, _) {
  1306.     var reValidName = /^[\w\-\$\:@\!%]+\+?$/i;
  1307.     var reWord = /[\w\-:\$]/;
  1308.     
  1309.     var pairs = {
  1310.         '[': ']',
  1311.         '(': ')',
  1312.         '{': '}'
  1313.     };
  1314.     
  1315.     var spliceFn = Array.prototype.splice;
  1316.     
  1317.     var preprocessors = [];
  1318.     var postprocessors = [];
  1319.     var outputProcessors = [];
  1320.     
  1321.     /**
  1322.      * @type AbbreviationNode
  1323.      */
  1324.     function AbbreviationNode(parent) {
  1325.         /** @type AbbreviationNode */
  1326.         this.parent = null;
  1327.         this.children = [];
  1328.         this._attributes = [];
  1329.         
  1330.         /** @type String Raw abbreviation for current node */
  1331.         this.abbreviation = '';
  1332.         this.counter = 1;
  1333.         this._name = null;
  1334.         this._text = '';
  1335.         this.repeatCount = 1;
  1336.         this.hasImplicitRepeat = false;
  1337.         
  1338.         /** Custom data dictionary */
  1339.         this._data = {};
  1340.         
  1341.         // output properties
  1342.         this.start = '';
  1343.         this.end = '';
  1344.         this.content = '';
  1345.         this.padding = '';
  1346.     }
  1347.     
  1348.     AbbreviationNode.prototype = {
  1349.         /**
  1350.          * Adds passed node as child or creates new child
  1351.          * @param {AbbreviationNode} child
  1352.          * @param {Number} position Index in children array where child should 
  1353.          * be inserted
  1354.          * @return {AbbreviationNode}
  1355.          */
  1356.         addChild: function(child, position) {
  1357.             child = child || new AbbreviationNode;
  1358.             child.parent = this;
  1359.             
  1360.             if (_.isUndefined(position)) {
  1361.                 this.children.push(child);
  1362.             } else {
  1363.                 this.children.splice(position, 0, child);
  1364.             }
  1365.             
  1366.             return child;
  1367.         },
  1368.         
  1369.         /**
  1370.          * Creates a deep copy of current node
  1371.          * @returns {AbbreviationNode}
  1372.          */
  1373.         clone: function() {
  1374.             var node = new AbbreviationNode();
  1375.             var attrs = ['abbreviation', 'counter', '_name', '_text', 'repeatCount', 'hasImplicitRepeat', 'start', 'end', 'content', 'padding'];
  1376.             _.each(attrs, function(a) {
  1377.                 node[a] = this[a];
  1378.             }, this);
  1379.             
  1380.             // clone attributes
  1381.             node._attributes = _.map(this._attributes, function(attr) {
  1382.                 return _.clone(attr);
  1383.             });
  1384.             
  1385.             node._data = _.clone(this._data);
  1386.             
  1387.             // clone children
  1388.             node.children = _.map(this.children, function(child) {
  1389.                 child = child.clone();
  1390.                 child.parent = node;
  1391.                 return child;
  1392.             });
  1393.             
  1394.             return node;
  1395.         },
  1396.         
  1397.         /**
  1398.          * Removes current node from parentΓÇÿs child list
  1399.          * @returns {AbbreviationNode} Current node itself
  1400.          */
  1401.         remove: function() {
  1402.             if (this.parent) {
  1403.                 this.parent.children = _.without(this.parent.children, this);
  1404.             }
  1405.             
  1406.             return this;
  1407.         },
  1408.         
  1409.         /**
  1410.          * Replaces current node in parentΓÇÿs children list with passed nodes
  1411.          * @param {AbbreviationNode} node Replacement node or array of nodes
  1412.          */
  1413.         replace: function() {
  1414.             var parent = this.parent;
  1415.             var ix = _.indexOf(parent.children, this);
  1416.             var items = _.flatten(arguments);
  1417.             spliceFn.apply(parent.children, [ix, 1].concat(items));
  1418.             
  1419.             // update parent
  1420.             _.each(items, function(item) {
  1421.                 item.parent = parent;
  1422.             });
  1423.         },
  1424.         
  1425.         /**
  1426.          * Recursively sets <code>property</code> to <code>value</code> of current
  1427.          * node and its children 
  1428.          * @param {String} name Property to update
  1429.          * @param {Object} value New property value
  1430.          */
  1431.         updateProperty: function(name, value) {
  1432.             this[name] = value;
  1433.             _.each(this.children, function(child) {
  1434.                 child.updateProperty(name, value);
  1435.             });
  1436.         },
  1437.         
  1438.         /**
  1439.          * Finds first child node that matches truth test for passed 
  1440.          * <code>fn</code> function
  1441.          * @param {Function} fn
  1442.          * @returns {AbbreviationNode}
  1443.          */
  1444.         find: function(fn) {
  1445.             return this.findAll(fn)[0];
  1446. //            if (!_.isFunction(fn)) {
  1447. //                var elemName = fn.toLowerCase();
  1448. //                fn = function(item) {return item.name().toLowerCase() == elemName;};
  1449. //            }
  1450. //            
  1451. //            var result = null;
  1452. //            _.find(this.children, function(child) {
  1453. //                if (fn(child)) {
  1454. //                    return result = child;
  1455. //                }
  1456. //                
  1457. //                return result = child.find(fn);
  1458. //            });
  1459. //            
  1460. //            return result;
  1461.         },
  1462.         
  1463.         /**
  1464.          * Finds all child nodes that matches truth test for passed 
  1465.          * <code>fn</code> function
  1466.          * @param {Function} fn
  1467.          * @returns {Array}
  1468.          */
  1469.         findAll: function(fn) {
  1470.             if (!_.isFunction(fn)) {
  1471.                 var elemName = fn.toLowerCase();
  1472.                 fn = function(item) {return item.name().toLowerCase() == elemName;};
  1473.             }
  1474.                 
  1475.             var result = [];
  1476.             _.each(this.children, function(child) {
  1477.                 if (fn(child))
  1478.                     result.push(child);
  1479.                 
  1480.                 result = result.concat(child.findAll(fn));
  1481.             });
  1482.             
  1483.             return _.compact(result);
  1484.         },
  1485.         
  1486.         /**
  1487.          * Sets/gets custom data
  1488.          * @param {String} name
  1489.          * @param {Object} value
  1490.          * @returns {Object}
  1491.          */
  1492.         data: function(name, value) {
  1493.             if (arguments.length == 2) {
  1494.                 this._data[name] = value;
  1495.                 
  1496.                 if (name == 'resource' && require('elements').is(value, 'snippet')) {
  1497.                     // setting snippet as matched resource: update `content`
  1498.                     // property with snippet value
  1499.                     this.content = value.data;
  1500.                     if (this._text) {
  1501.                         this.content = require('abbreviationUtils')
  1502.                             .insertChildContent(value.data, this._text);
  1503.                     }
  1504.                 }
  1505.             }
  1506.             
  1507.             return this._data[name];
  1508.         },
  1509.         
  1510.         /**
  1511.          * Returns name of current node
  1512.          * @returns {String}
  1513.          */
  1514.         name: function() {
  1515.             var res = this.matchedResource();
  1516.             if (require('elements').is(res, 'element')) {
  1517.                 return res.name;
  1518.             }
  1519.             
  1520.             return this._name;
  1521.         },
  1522.         
  1523.         /**
  1524.          * Returns list of attributes for current node
  1525.          * @returns {Array}
  1526.          */
  1527.         attributeList: function() {
  1528.             var attrs = [];
  1529.             
  1530.             var res = this.matchedResource();
  1531.             if (require('elements').is(res, 'element') && _.isArray(res.attributes)) {
  1532.                 attrs = attrs.concat(res.attributes);
  1533.             }
  1534.             
  1535.             return optimizeAttributes(attrs.concat(this._attributes));
  1536.         },
  1537.         
  1538.         /**
  1539.          * Returns or sets attribute value
  1540.          * @param {String} name Attribute name
  1541.          * @param {String} value New attribute value
  1542.          * @returns {String}
  1543.          */
  1544.         attribute: function(name, value) {
  1545.             if (arguments.length == 2) {
  1546.                 // modifying attribute
  1547.                 var ix = _.indexOf(_.pluck(this._attributes, 'name'), name.toLowerCase());
  1548.                 if (~ix) {
  1549.                     this._attributes[ix].value = value;
  1550.                 } else {
  1551.                     this._attributes.push({
  1552.                         name: name,
  1553.                         value: value
  1554.                     });
  1555.                 }
  1556.             }
  1557.             
  1558.             return (_.find(this.attributeList(), function(attr) {
  1559.                 return attr.name == name;
  1560.             }) || {}).value;
  1561.         },
  1562.         
  1563.         /**
  1564.          * Returns reference to the matched <code>element</code>, if any.
  1565.          * See {@link elements} module for a list of available elements
  1566.          * @returns {Object}
  1567.          */
  1568.         matchedResource: function() {
  1569.             return this.data('resource');
  1570.         },
  1571.         
  1572.         /**
  1573.          * Returns index of current node in parentΓÇÿs children list
  1574.          * @returns {Number}
  1575.          */
  1576.         index: function() {
  1577.             return this.parent ? _.indexOf(this.parent.children, this) : -1;
  1578.         },
  1579.         
  1580.         /**
  1581.          * Sets how many times current element should be repeated
  1582.          * @private
  1583.          */
  1584.         _setRepeat: function(count) {
  1585.             if (count) {
  1586.                 this.repeatCount = parseInt(count, 10) || 1;
  1587.             } else {
  1588.                 this.hasImplicitRepeat = true;
  1589.             }
  1590.         },
  1591.         
  1592.         /**
  1593.          * Sets abbreviation that belongs to current node
  1594.          * @param {String} abbr
  1595.          */
  1596.         setAbbreviation: function(abbr) {
  1597.             abbr = abbr || '';
  1598.             
  1599.             var that = this;
  1600.             
  1601.             // find multiplier
  1602.             abbr = abbr.replace(/\*(\d+)?$/, function(str, repeatCount) {
  1603.                 that._setRepeat(repeatCount);
  1604.                 return '';
  1605.             });
  1606.             
  1607.             this.abbreviation = abbr;
  1608.             
  1609.             var abbrText = extractText(abbr);
  1610.             if (abbrText) {
  1611.                 abbr = abbrText.element;
  1612.                 this.content = this._text = abbrText.text;
  1613.             }
  1614.             
  1615.             var abbrAttrs = parseAttributes(abbr);
  1616.             if (abbrAttrs) {
  1617.                 abbr = abbrAttrs.element;
  1618.                 this._attributes = abbrAttrs.attributes;
  1619.             }
  1620.             
  1621.             this._name = abbr;
  1622.             
  1623.             // validate name
  1624.             if (this._name && !reValidName.test(this._name)) {
  1625.                 throw 'Invalid abbreviation';
  1626.             }
  1627.         },
  1628.         
  1629.         /**
  1630.          * Returns string representation of current node
  1631.          * @return {String}
  1632.          */
  1633.         toString: function() {
  1634.             var utils = require('utils');
  1635.             
  1636.             var start = this.start;
  1637.             var end = this.end;
  1638.             var content = this.content;
  1639.             
  1640.             // apply output processors
  1641.             var node = this;
  1642.             _.each(outputProcessors, function(fn) {
  1643.                 start = fn(start, node, 'start');
  1644.                 content = fn(content, node, 'content');
  1645.                 end = fn(end, node, 'end');
  1646.             });
  1647.             
  1648.             
  1649.             var innerContent = _.map(this.children, function(child) {
  1650.                 return child.toString();
  1651.             }).join('');
  1652.             
  1653.             content = require('abbreviationUtils').insertChildContent(content, innerContent, {
  1654.                 keepVariable: false
  1655.             });
  1656.             
  1657.             return start + utils.padString(content, this.padding) + end;
  1658.         },
  1659.         
  1660.         /**
  1661.          * Check if current node contains children with empty <code>expr</code>
  1662.          * property
  1663.          * @return {Boolean}
  1664.          */
  1665.         hasEmptyChildren: function() {
  1666.             return !!_.find(this.children, function(child) {
  1667.                 return child.isEmpty();
  1668.             });
  1669.         },
  1670.         
  1671.         /**
  1672.          * Check if current node has implied name that should be resolved
  1673.          * @returns {Boolean}
  1674.          */
  1675.         hasImplicitName: function() {
  1676.             return !this._name && !this.isTextNode();
  1677.         },
  1678.         
  1679.         /**
  1680.          * Indicates that current element is a grouping one, e.g. has no 
  1681.          * representation but serves as a container for other nodes
  1682.          * @returns {Boolean}
  1683.          */
  1684.         isGroup: function() {
  1685.             return !this.abbreviation;
  1686.         },
  1687.         
  1688.         /**
  1689.          * Indicates empty node (i.e. without abbreviation). It may be a 
  1690.          * grouping node and should not be outputted
  1691.          * @return {Boolean}
  1692.          */
  1693.         isEmpty: function() {
  1694.             return !this.abbreviation && !this.children.length;
  1695.         },
  1696.         
  1697.         /**
  1698.          * Indicates that current node should be repeated
  1699.          * @returns {Boolean}
  1700.          */
  1701.         isRepeating: function() {
  1702.             return this.repeatCount > 1 || this.hasImplicitRepeat;
  1703.         },
  1704.         
  1705.         /**
  1706.          * Check if current node is a text-only node
  1707.          * @return {Boolean}
  1708.          */
  1709.         isTextNode: function() {
  1710.             return !this.name() && !this.attributeList().length;
  1711.         },
  1712.         
  1713.         /**
  1714.          * Indicates whether this node may be used to build elements or snippets
  1715.          * @returns {Boolean}
  1716.          */
  1717.         isElement: function() {
  1718.             return !this.isEmpty() && !this.isTextNode();
  1719.         },
  1720.         
  1721.         /**
  1722.          * Returns latest and deepest child of current tree
  1723.          * @returns {AbbreviationNode}
  1724.          */
  1725.         deepestChild: function() {
  1726.             if (!this.children.length)
  1727.                 return null;
  1728.                 
  1729.             var deepestChild = this;
  1730.             while (deepestChild.children.length) {
  1731.                 deepestChild = _.last(deepestChild.children);
  1732.             }
  1733.             
  1734.             return deepestChild;
  1735.         }
  1736.     };
  1737.     
  1738.     /**
  1739.      * Returns stripped string: a string without first and last character.
  1740.      * Used for ΓÇ£unquotingΓÇ¥ strings
  1741.      * @param {String} str
  1742.      * @returns {String}
  1743.      */
  1744.     function stripped(str) {
  1745.         return str.substring(1, str.length - 1);
  1746.     }
  1747.     
  1748.     function consumeQuotedValue(stream, quote) {
  1749.         var ch;
  1750.         while (ch = stream.next()) {
  1751.             if (ch === quote)
  1752.                 return true;
  1753.             
  1754.             if (ch == '\\')
  1755.                 continue;
  1756.         }
  1757.         
  1758.         return false;
  1759.     }
  1760.     
  1761.     /**
  1762.      * Parses abbreviation into a tree
  1763.      * @param {String} abbr
  1764.      * @returns {AbbreviationNode}
  1765.      */
  1766.     function parseAbbreviation(abbr) {
  1767.         abbr = require('utils').trim(abbr);
  1768.         
  1769.         var root = new AbbreviationNode;
  1770.         var context = root.addChild(), ch;
  1771.         
  1772.         /** @type StringStream */
  1773.         var stream = require('stringStream').create(abbr);
  1774.         var loopProtector = 1000, multiplier;
  1775.         
  1776.         while (!stream.eol() && --loopProtector > 0) {
  1777.             ch = stream.peek();
  1778.             
  1779.             switch (ch) {
  1780.                 case '(': // abbreviation group
  1781.                     stream.start = stream.pos;
  1782.                     if (stream.skipToPair('(', ')')) {
  1783.                         var inner = parseAbbreviation(stripped(stream.current()));
  1784.                         if (multiplier = stream.match(/^\*(\d+)?/, true)) {
  1785.                             context._setRepeat(multiplier[1]);
  1786.                         }
  1787.                         
  1788.                         _.each(inner.children, function(child) {
  1789.                             context.addChild(child);
  1790.                         });
  1791.                     } else {
  1792.                         throw 'Invalid abbreviation: mo matching ")" found for character at ' + stream.pos;
  1793.                     }
  1794.                     break;
  1795.                     
  1796.                 case '>': // child operator
  1797.                     context = context.addChild();
  1798.                     stream.next();
  1799.                     break;
  1800.                     
  1801.                 case '+': // sibling operator
  1802.                     context = context.parent.addChild();
  1803.                     stream.next();
  1804.                     break;
  1805.                     
  1806.                 case '^': // climb up operator
  1807.                     var parent = context.parent || context;
  1808.                     context = (parent.parent || parent).addChild();
  1809.                     stream.next();
  1810.                     break;
  1811.                     
  1812.                 default: // consume abbreviation
  1813.                     stream.start = stream.pos;
  1814.                     stream.eatWhile(function(c) {
  1815.                         if (c == '[' || c == '{') {
  1816.                             if (stream.skipToPair(c, pairs[c])) {
  1817.                                 stream.backUp(1);
  1818.                                 return true;
  1819.                             }
  1820.                             
  1821.                             throw 'Invalid abbreviation: mo matching "' + pairs[c] + '" found for character at ' + stream.pos;
  1822.                         }
  1823.                         
  1824.                         if (c == '+') {
  1825.                             // let's see if this is an expando marker
  1826.                             stream.next();
  1827.                             var isMarker = stream.eol() ||  ~'+>^*'.indexOf(stream.peek());
  1828.                             stream.backUp(1);
  1829.                             return isMarker;
  1830.                         }
  1831.                         
  1832.                         return c != '(' && isAllowedChar(c);
  1833.                     });
  1834.                     
  1835.                     context.setAbbreviation(stream.current());
  1836.                     stream.start = stream.pos;
  1837.             }
  1838.         }
  1839.         
  1840.         if (loopProtector < 1)
  1841.             throw 'Endless loop detected';
  1842.         
  1843.         return root;
  1844.     }
  1845.     
  1846.     /**
  1847.      * Extract attributes and their values from attribute set: 
  1848.      * <code>[attr col=3 title="Quoted string"]</code>
  1849.      * @param {String} attrSet
  1850.      * @returns {Array}
  1851.      */
  1852.     function extractAttributes(attrSet, attrs) {
  1853.         attrSet = require('utils').trim(attrSet);
  1854.         var result = [];
  1855.         
  1856.         /** @type StringStream */
  1857.         var stream = require('stringStream').create(attrSet);
  1858.         stream.eatSpace();
  1859.         
  1860.         while (!stream.eol()) {
  1861.             stream.start = stream.pos;
  1862.             if (stream.eatWhile(reWord)) {
  1863.                 var attrName = stream.current();
  1864.                 var attrValue = '';
  1865.                 if (stream.peek() == '=') {
  1866.                     stream.next();
  1867.                     stream.start = stream.pos;
  1868.                     var quote = stream.peek();
  1869.                     
  1870.                     if (quote == '"' || quote == "'") {
  1871.                         stream.next();
  1872.                         if (consumeQuotedValue(stream, quote)) {
  1873.                             attrValue = stream.current();
  1874.                             // strip quotes
  1875.                             attrValue = attrValue.substring(1, attrValue.length - 1);
  1876.                         } else {
  1877.                             throw 'Invalid attribute value';
  1878.                         }
  1879.                     } else if (stream.eatWhile(/[^\s\]]/)) {
  1880.                         attrValue = stream.current();
  1881.                     } else {
  1882.                         throw 'Invalid attribute value';
  1883.                     }
  1884.                 }
  1885.                 
  1886.                 result.push({
  1887.                     name: attrName, 
  1888.                     value: attrValue
  1889.                 });
  1890.                 stream.eatSpace();
  1891.             } else {
  1892.                 break;
  1893.             }
  1894.         }
  1895.         
  1896.         return result;
  1897.     }
  1898.     
  1899.     /**
  1900.      * Parses tag attributes extracted from abbreviation. If attributes found, 
  1901.      * returns object with <code>element</code> and <code>attributes</code>
  1902.      * properties
  1903.      * @param {String} abbr
  1904.      * @returns {Object} Returns <code>null</code> if no attributes found in 
  1905.      * abbreviation
  1906.      */
  1907.     function parseAttributes(abbr) {
  1908.         /*
  1909.          * Example of incoming data:
  1910.          * #header
  1911.          * .some.data
  1912.          * .some.data#header
  1913.          * [attr]
  1914.          * #item[attr=Hello other="World"].class
  1915.          */
  1916.         var result = [];
  1917.         var attrMap = {'#': 'id', '.': 'class'};
  1918.         var nameEnd = null;
  1919.         
  1920.         /** @type StringStream */
  1921.         var stream = require('stringStream').create(abbr);
  1922.         while (!stream.eol()) {
  1923.             switch (stream.peek()) {
  1924.                 case '#': // id
  1925.                 case '.': // class
  1926.                     if (nameEnd === null)
  1927.                         nameEnd = stream.pos;
  1928.                     
  1929.                     var attrName = attrMap[stream.peek()];
  1930.                     
  1931.                     stream.next();
  1932.                     stream.start = stream.pos;
  1933.                     stream.eatWhile(reWord);
  1934.                     result.push({
  1935.                         name: attrName, 
  1936.                         value: stream.current()
  1937.                     });
  1938.                     break;
  1939.                 case '[': //begin attribute set
  1940.                     if (nameEnd === null)
  1941.                         nameEnd = stream.pos;
  1942.                     
  1943.                     stream.start = stream.pos;
  1944.                     if (!stream.skipToPair('[', ']')) 
  1945.                         throw 'Invalid attribute set definition';
  1946.                     
  1947.                     result = result.concat(
  1948.                         extractAttributes(stripped(stream.current()))
  1949.                     );
  1950.                     break;
  1951.                 default:
  1952.                     stream.next();
  1953.             }
  1954.         }
  1955.         
  1956.         if (!result.length)
  1957.             return null;
  1958.         
  1959.         return {
  1960.             element: abbr.substring(0, nameEnd),
  1961.             attributes: optimizeAttributes(result)
  1962.         };
  1963.     }
  1964.     
  1965.     /**
  1966.      * Optimize attribute set: remove duplicates and merge class attributes
  1967.      * @param attrs
  1968.      */
  1969.     function optimizeAttributes(attrs) {
  1970.         // clone all attributes to make sure that original objects are 
  1971.         // not modified
  1972.         attrs  = _.map(attrs, function(attr) {
  1973.             return _.clone(attr);
  1974.         });
  1975.         
  1976.         var lookup = {};
  1977.         return _.filter(attrs, function(attr) {
  1978.             if (!(attr.name in lookup)) {
  1979.                 return lookup[attr.name] = attr;
  1980.             }
  1981.             
  1982.             var la = lookup[attr.name];
  1983.             
  1984.             if (attr.name.toLowerCase() == 'class') {
  1985.                 la.value += (la.value.length ? ' ' : '') + attr.value;
  1986.             } else {
  1987.                 la.value = attr.value;
  1988.             }
  1989.             
  1990.             return false;
  1991.         });
  1992.     }
  1993.     
  1994.     /**
  1995.      * Extract text data from abbreviation: if <code>a{hello}</code> abbreviation
  1996.      * is passed, returns object <code>{element: 'a', text: 'hello'}</code>.
  1997.      * If nothing found, returns <code>null</code>
  1998.      * @param {String} abbr
  1999.      * 
  2000.      */
  2001.     function extractText(abbr) {
  2002.         if (!~abbr.indexOf('{'))
  2003.             return null;
  2004.         
  2005.         /** @type StringStream */
  2006.         var stream = require('stringStream').create(abbr);
  2007.         while (!stream.eol()) {
  2008.             switch (stream.peek()) {
  2009.                 case '[':
  2010.                 case '(':
  2011.                     stream.skipToPair(stream.peek(), pairs[stream.peek()]); break;
  2012.                     
  2013.                 case '{':
  2014.                     stream.start = stream.pos;
  2015.                     stream.skipToPair('{', '}');
  2016.                     return {
  2017.                         element: abbr.substring(0, stream.start),
  2018.                         text: stripped(stream.current())
  2019.                     };
  2020.                     
  2021.                 default:
  2022.                     stream.next();
  2023.             }
  2024.         }
  2025.     }
  2026.     
  2027.     /**
  2028.      * ΓÇ£Un-rollsΓÇ£ contents of current node: recursively replaces all repeating 
  2029.      * children with their repeated clones
  2030.      * @param {AbbreviationNode} node
  2031.      * @returns {AbbreviationNode}
  2032.      */
  2033.     function unroll(node) {
  2034.         for (var i = node.children.length - 1, j, child; i >= 0; i--) {
  2035.             child = node.children[i];
  2036.             
  2037.             if (child.isRepeating()) {
  2038.                 j = child.repeatCount;
  2039.                 child.repeatCount = 1;
  2040.                 child.updateProperty('counter', 1);
  2041.                 while (--j > 0) {
  2042.                     child.parent.addChild(child.clone(), i + 1)
  2043.                         .updateProperty('counter', j + 1);
  2044.                 }
  2045.             }
  2046.         }
  2047.         
  2048.         // to keep proper 'counter' property, we need to walk
  2049.         // on children once again
  2050.         _.each(node.children, unroll);
  2051.         
  2052.         return node;
  2053.     }
  2054.     
  2055.     /**
  2056.      * Optimizes tree node: replaces empty nodes with their children
  2057.      * @param {AbbreviationNode} node
  2058.      * @return {AbbreviationNode}
  2059.      */
  2060.     function squash(node) {
  2061.         for (var i = node.children.length - 1; i >= 0; i--) {
  2062.             /** @type AbbreviationNode */
  2063.             var n = node.children[i];
  2064.             if (n.isGroup()) {
  2065.                 n.replace(squash(n).children);
  2066.             } else if (n.isEmpty()) {
  2067.                 n.remove();
  2068.             }
  2069.         }
  2070.         
  2071.         _.each(node.children, squash);
  2072.         
  2073.         return node;
  2074.     }
  2075.     
  2076.     function isAllowedChar(ch) {
  2077.         var charCode = ch.charCodeAt(0);
  2078.         var specialChars = '#.*:$-_!@|%';
  2079.         
  2080.         return (charCode > 64 && charCode < 91)       // uppercase letter
  2081.                 || (charCode > 96 && charCode < 123)  // lowercase letter
  2082.                 || (charCode > 47 && charCode < 58)   // number
  2083.                 || specialChars.indexOf(ch) != -1;    // special character
  2084.     }
  2085.     
  2086.     // XXX add counter replacer function as output processor
  2087.     outputProcessors.push(function(text, node) {
  2088.         return require('utils').replaceCounter(text, node.counter);
  2089.     });
  2090.     
  2091.     return {
  2092.         /**
  2093.          * Parses abbreviation into tree with respect of groups, 
  2094.          * text nodes and attributes. Each node of the tree is a single 
  2095.          * abbreviation. Tree represents actual structure of the outputted 
  2096.          * result
  2097.          * @memberOf abbreviationParser
  2098.          * @param {String} abbr Abbreviation to parse
  2099.          * @param {Object} options Additional options for parser and processors
  2100.          * 
  2101.          * @return {AbbreviationNode}
  2102.          */
  2103.         parse: function(abbr, options) {
  2104.             options = options || {};
  2105.             
  2106.             var tree = parseAbbreviation(abbr);
  2107.             
  2108.             if (options.contextNode) {
  2109.                 // add info about context node ΓÇô
  2110.                 // a parent XHTML node in editor inside which abbreviation is 
  2111.                 // expanded
  2112.                 tree._name = options.contextNode.name;
  2113.                 var attrLookup = {};
  2114.                 _.each(tree._attributes, function(attr) {
  2115.                     attrLookup[attr.name] = attr;
  2116.                 });
  2117.                 
  2118.                 _.each(options.contextNode.attributes, function(attr) {
  2119.                     if (attr.name in attrLookup) {
  2120.                         attrLookup[attr.name].value = attr.value;
  2121.                     } else {
  2122.                         attr = _.clone(attr);
  2123.                         tree._attributes.push(attr);
  2124.                         attrLookup[attr.name] = attr;
  2125.                     }
  2126.                 });
  2127.             }
  2128.             
  2129.             
  2130.             // apply preprocessors
  2131.             _.each(preprocessors, function(fn) {
  2132.                 fn(tree, options);
  2133.             });
  2134.             
  2135.             tree = squash(unroll(tree));
  2136.             
  2137.             // apply postprocessors
  2138.             _.each(postprocessors, function(fn) {
  2139.                 fn(tree, options);
  2140.             });
  2141.             
  2142.             return tree;
  2143.         },
  2144.         
  2145.         AbbreviationNode: AbbreviationNode,
  2146.         
  2147.         /**
  2148.          * Add new abbreviation preprocessor. <i>Preprocessor</i> is a function
  2149.          * that applies to a parsed abbreviation tree right after it get parsed.
  2150.          * The passed tree is in unoptimized state.
  2151.          * @param {Function} fn Preprocessor function. This function receives
  2152.          * two arguments: parsed abbreviation tree (<code>AbbreviationNode</code>)
  2153.          * and <code>options</code> hash that was passed to <code>parse</code>
  2154.          * method
  2155.          */
  2156.         addPreprocessor: function(fn) {
  2157.             if (!_.include(preprocessors, fn))
  2158.                 preprocessors.push(fn);
  2159.         },
  2160.         
  2161.         /**
  2162.          * Removes registered preprocessor
  2163.          */
  2164.         removeFilter: function(fn) {
  2165.             preprocessor = _.without(preprocessors, fn);
  2166.         },
  2167.         
  2168.         /**
  2169.          * Adds new abbreviation postprocessor. <i>Postprocessor</i> is a 
  2170.          * functinon that applies to <i>optimized</i> parsed abbreviation tree
  2171.          * right before it returns from <code>parse()</code> method
  2172.          * @param {Function} fn Postprocessor function. This function receives
  2173.          * two arguments: parsed abbreviation tree (<code>AbbreviationNode</code>)
  2174.          * and <code>options</code> hash that was passed to <code>parse</code>
  2175.          * method
  2176.          */
  2177.         addPostprocessor: function(fn) {
  2178.             if (!_.include(postprocessors, fn))
  2179.                 postprocessors.push(fn);
  2180.         },
  2181.         
  2182.         /**
  2183.          * Removes registered postprocessor function
  2184.          */
  2185.         removePostprocessor: function(fn) {
  2186.             postprocessors = _.without(postprocessors, fn);
  2187.         },
  2188.         
  2189.         /**
  2190.          * Registers output postprocessor. <i>Output processor</i> is a 
  2191.          * function that applies to output part (<code>start</code>, 
  2192.          * <code>end</code> and <code>content</code>) when 
  2193.          * <code>AbbreviationNode.toString()</code> method is called
  2194.          */
  2195.         addOutputProcessor: function(fn) {
  2196.             if (!_.include(outputProcessors, fn))
  2197.                 outputProcessors.push(fn);
  2198.         },
  2199.         
  2200.         /**
  2201.          * Removes registered output processor
  2202.          */
  2203.         removeOutputProcessor: function(fn) {
  2204.             outputProcessors = _.without(outputProcessors, fn);
  2205.         },
  2206.         
  2207.         /**
  2208.          * Check if passed symbol is valid symbol for abbreviation expression
  2209.          * @param {String} ch
  2210.          * @return {Boolean}
  2211.          */
  2212.         isAllowedChar: function(ch) {
  2213.             ch = String(ch); // convert Java object to JS
  2214.             return isAllowedChar(ch) || ~'>+^[](){}'.indexOf(ch);
  2215.         }
  2216.     };
  2217. });/**
  2218.  * Processor function that matches parsed <code>AbbreviationNode</code>
  2219.  * against resources defined in <code>resource</code> module
  2220.  * @param {Function} require
  2221.  * @param {Underscore} _
  2222.  */ 
  2223. emmet.exec(function(require, _) {
  2224.     /**
  2225.      * Finds matched resources for child nodes of passed <code>node</code> 
  2226.      * element. A matched resource is a reference to <i>snippets.json</i> entry
  2227.      * that describes output of parsed node 
  2228.      * @param {AbbreviationNode} node
  2229.      * @param {String} syntax
  2230.      */
  2231.     function matchResources(node, syntax) {
  2232.         var resources = require('resources');
  2233.         var elements = require('elements');
  2234.         var parser = require('abbreviationParser');
  2235.         
  2236.         // do a shallow copy because the children list can be modified during
  2237.         // resource matching
  2238.         _.each(_.clone(node.children), /** @param {AbbreviationNode} child */ function(child) {
  2239.             var r = resources.getMatchedResource(child, syntax);
  2240.             if (_.isString(r)) {
  2241.                 child.data('resource', elements.create('snippet', r));
  2242.             } else if (elements.is(r, 'reference')) {
  2243.                 // itΓÇÖs a reference to another abbreviation:
  2244.                 // parse it and insert instead of current child
  2245.                 /** @type AbbreviationNode */
  2246.                 var subtree = parser.parse(r.data, {
  2247.                     syntax: syntax
  2248.                 });
  2249.                 
  2250.                 // if context element should be repeated, check if we need to 
  2251.                 // transfer repeated element to specific child node
  2252.                 if (child.repeatCount > 1) {
  2253.                     var repeatedChildren = subtree.findAll(function(node) {
  2254.                         return node.hasImplicitRepeat;
  2255.                     });
  2256.                     
  2257.                     _.each(repeatedChildren, function(node) {
  2258.                         node.repeatCount = child.repeatCount;
  2259.                         node.hasImplicitRepeat = false;
  2260.                     });
  2261.                 }
  2262.                 
  2263.                 // move childΓÇÿs children into the deepest child of new subtree
  2264.                 var deepestChild = subtree.deepestChild();
  2265.                 if (deepestChild) {
  2266.                     _.each(child.children, function(c) {
  2267.                         deepestChild.addChild(c);
  2268.                     });
  2269.                 }
  2270.                 
  2271.                 // copy current attributes to children
  2272.                 _.each(subtree.children, function(node) {
  2273.                     _.each(child.attributeList(), function(attr) {
  2274.                         node.attribute(attr.name, attr.value);
  2275.                     });
  2276.                 });
  2277.                 
  2278.                 child.replace(subtree.children);
  2279.             } else {
  2280.                 child.data('resource', r);
  2281.             }
  2282.             
  2283.             matchResources(child, syntax);
  2284.         });
  2285.     }
  2286.     
  2287.     // XXX register abbreviation filter that creates references to resources
  2288.     // on abbreviation nodes
  2289.     /**
  2290.      * @param {AbbreviationNode} tree
  2291.      */
  2292.     require('abbreviationParser').addPreprocessor(function(tree, options) {
  2293.         var syntax = options.syntax || emmet.defaultSyntax();
  2294.         matchResources(tree, syntax);
  2295.     });
  2296.     
  2297. });/**
  2298.  * Pasted content abbreviation processor. A pasted content is a content that
  2299.  * should be inserted into implicitly repeated abbreviation nodes.
  2300.  * This processor powers ΓÇ£Wrap With AbbreviationΓÇ¥ action
  2301.  * @param {Function} require
  2302.  * @param {Underscore} _
  2303.  */
  2304. emmet.exec(function(require, _) {
  2305.     var parser = require('abbreviationParser');
  2306.     var outputPlaceholder = '$#';
  2307.     
  2308.     /**
  2309.      * Locates output placeholders inside text
  2310.      * @param {String} text
  2311.      * @returns {Array} Array of ranges of output placeholder in text
  2312.      */
  2313.     function locateOutputPlaceholder(text) {
  2314.         var range = require('range');
  2315.         var result = [];
  2316.         
  2317.         /** @type StringStream */
  2318.         var stream = require('stringStream').create(text);
  2319.         
  2320.         while (!stream.eol()) {
  2321.             if (stream.peek() == '\\') {
  2322.                 stream.next();
  2323.             } else {
  2324.                 stream.start = stream.pos;
  2325.                 if (stream.match(outputPlaceholder, true)) {
  2326.                     result.push(range.create(stream.start, outputPlaceholder));
  2327.                     continue;
  2328.                 }
  2329.             }
  2330.             stream.next();
  2331.         }
  2332.         
  2333.         return result;
  2334.     }
  2335.     
  2336.     /**
  2337.      * Replaces output placeholders inside <code>source</code> with 
  2338.      * <code>value</code>
  2339.      * @param {String} source
  2340.      * @param {String} value
  2341.      * @returns {String}
  2342.      */
  2343.     function replaceOutputPlaceholders(source, value) {
  2344.         var utils = require('utils');
  2345.         var ranges = locateOutputPlaceholder(source);
  2346.         
  2347.         ranges.reverse();
  2348.         _.each(ranges, function(r) {
  2349.             source = utils.replaceSubstring(source, value, r);
  2350.         });
  2351.         
  2352.         return source;
  2353.     }
  2354.     
  2355.     /**
  2356.      * Check if parsed node contains output placeholder ΓÇô a target where
  2357.      * pasted content should be inserted
  2358.      * @param {AbbreviationNode} node
  2359.      * @returns {Boolean}
  2360.      */
  2361.     function hasOutputPlaceholder(node) {
  2362.         if (locateOutputPlaceholder(node.content).length)
  2363.             return true;
  2364.         
  2365.         // check if attributes contains placeholder
  2366.         return !!_.find(node.attributeList(), function(attr) {
  2367.             return !!locateOutputPlaceholder(attr.value).length;
  2368.         });
  2369.     }
  2370.     
  2371.     /**
  2372.      * Insert pasted content into correct positions of parsed node
  2373.      * @param {AbbreviationNode} node
  2374.      * @param {String} content
  2375.      * @param {Boolean} overwrite Overwrite node content if no value placeholders
  2376.      * found instead of appending to existing content
  2377.      */
  2378.     function insertPastedContent(node, content, overwrite) {
  2379.         var nodesWithPlaceholders = node.findAll(function(item) {
  2380.             return hasOutputPlaceholder(item);
  2381.         });
  2382.         
  2383.         if (hasOutputPlaceholder(node))
  2384.             nodesWithPlaceholders.unshift(node);
  2385.         
  2386.         if (nodesWithPlaceholders.length) {
  2387.             _.each(nodesWithPlaceholders, function(item) {
  2388.                 item.content = replaceOutputPlaceholders(item.content, content);
  2389.                 _.each(item._attributes, function(attr) {
  2390.                     attr.value = replaceOutputPlaceholders(attr.value, content);
  2391.                 });
  2392.             });
  2393.         } else {
  2394.             // on output placeholders in subtree, insert content in the deepest
  2395.             // child node
  2396.             var deepest = node.deepestChild() || node;
  2397.             if (overwrite) {
  2398.                 deepest.content = content;
  2399.             } else {
  2400.                 deepest.content = require('abbreviationUtils').insertChildContent(deepest.content, content);
  2401.             }
  2402.         }
  2403.     }
  2404.     
  2405.     /**
  2406.      * @param {AbbreviationNode} tree
  2407.      * @param {Object} options
  2408.      */
  2409.     parser.addPreprocessor(function(tree, options) {
  2410.         if (options.pastedContent) {
  2411.             var utils = require('utils');
  2412.             var lines = _.map(utils.splitByLines(options.pastedContent, true), utils.trim);
  2413.             
  2414.             // set repeat count for implicitly repeated elements before
  2415.             // tree is unrolled
  2416.             tree.findAll(function(item) {
  2417.                 if (item.hasImplicitRepeat) {
  2418.                     item.data('paste', lines);
  2419.                     return item.repeatCount = lines.length;
  2420.                 }
  2421.             });
  2422.         }
  2423.     });
  2424.     
  2425.     /**
  2426.      * @param {AbbreviationNode} tree
  2427.      * @param {Object} options
  2428.      */
  2429.     parser.addPostprocessor(function(tree, options) {
  2430.         // for each node with pasted content, update text data
  2431.         var targets = tree.findAll(function(item) {
  2432.             var pastedContentObj = item.data('paste');
  2433.             var pastedContent = '';
  2434.             if (_.isArray(pastedContentObj)) {
  2435.                 pastedContent = pastedContentObj[item.counter - 1];
  2436.             } else if (_.isFunction(pastedContentObj)) {
  2437.                 pastedContent = pastedContentObj(item.counter - 1, item.content);
  2438.             } else if (pastedContentObj) {
  2439.                 pastedContent = pastedContentObj;
  2440.             }
  2441.             
  2442.             if (pastedContent) {
  2443.                 insertPastedContent(item, pastedContent, !!item.data('pasteOverwrites'));
  2444.             }
  2445.             
  2446.             item.data('paste', null);
  2447.             return !!pastedContentObj;
  2448.         });
  2449.         
  2450.         if (!targets.length && options.pastedContent) {
  2451.             // no implicitly repeated elements, put pasted content in
  2452.             // the deepest child
  2453.             insertPastedContent(tree, options.pastedContent);
  2454.         }
  2455.     });
  2456. });/**
  2457.  * Resolves tag names in abbreviations with implied name
  2458.  */
  2459. emmet.exec(function(require, _) {
  2460.     /**
  2461.      * Resolves implicit node names in parsed tree
  2462.      * @param {AbbreviationNode} tree
  2463.      */
  2464.     function resolveNodeNames(tree) {
  2465.         var tagName = require('tagName');
  2466.         _.each(tree.children, function(node) {
  2467.             if (node.hasImplicitName() || node.data('forceNameResolving')) {
  2468.                 node._name = tagName.resolve(node.parent.name());
  2469.             }
  2470.             resolveNodeNames(node);
  2471.         });
  2472.         
  2473.         return tree;
  2474.     }
  2475.     
  2476.     require('abbreviationParser').addPostprocessor(resolveNodeNames);
  2477. });/**
  2478.  * @author Stoyan Stefanov
  2479.  * @link https://github.com/stoyan/etc/tree/master/cssex
  2480.  */
  2481.  
  2482. emmet.define('cssParser', function(require, _) {
  2483. var walker, tokens = [], isOp, isNameChar, isDigit;
  2484.     
  2485.     // walks around the source
  2486.     walker = {
  2487.         lines: null,
  2488.         total_lines: 0,
  2489.         linenum: -1,
  2490.         line: '',
  2491.         ch: '',
  2492.         chnum: -1,
  2493.         init: function (source) {
  2494.             var me = walker;
  2495.         
  2496.             // source, yumm
  2497.             me.lines = source
  2498.                 .replace(/\r\n/g, '\n')
  2499.                 .replace(/\r/g, '\n')
  2500.                 .split('\n');
  2501.             me.total_lines = me.lines.length;
  2502.         
  2503.             // reset
  2504.             me.chnum = -1;
  2505.             me.linenum = -1;
  2506.             me.ch = '';
  2507.             me.line = '';
  2508.         
  2509.             // advance
  2510.             me.nextLine();
  2511.             me.nextChar();
  2512.         },
  2513.         nextLine: function () {
  2514.             var me = this;
  2515.             me.linenum += 1;
  2516.             if (me.total_lines <= me.linenum) {
  2517.                 me.line = false;
  2518.             } else {
  2519.                 me.line = me.lines[me.linenum];
  2520.             }
  2521.             if (me.chnum !== -1) {
  2522.                 me.chnum = 0;
  2523.             }
  2524.             return me.line;
  2525.         }, 
  2526.         nextChar: function () {
  2527.             var me = this;
  2528.             me.chnum += 1;
  2529.             while (me.line.charAt(me.chnum) === '') {
  2530.                 if (this.nextLine() === false) {
  2531.                     me.ch = false;
  2532.                     return false; // end of source
  2533.                 }
  2534.                 me.chnum = -1;
  2535.                 me.ch = '\n';
  2536.                 return '\n';
  2537.             }
  2538.             me.ch = me.line.charAt(me.chnum);
  2539.             return me.ch;
  2540.         },
  2541.         peek: function() {
  2542.             return this.line.charAt(this.chnum + 1);
  2543.         }
  2544.     };
  2545.  
  2546.     // utility helpers
  2547.     isNameChar = function (c) {
  2548.         // be more tolerate for name tokens: allow & character for LESS syntax
  2549.         return (c == '&' || c === '_' || c === '-' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
  2550.     };
  2551.  
  2552.     isDigit = function (ch) {
  2553.         return (ch !== false && ch >= '0' && ch <= '9');
  2554.     };  
  2555.  
  2556.     isOp = (function () {
  2557.         var opsa = "{}[]()+*=.,;:>~|\\%$#@^!".split(''),
  2558.             opsmatcha = "*^|$~".split(''),
  2559.             ops = {},
  2560.             opsmatch = {},
  2561.             i = 0;
  2562.         for (; i < opsa.length; i += 1) {
  2563.             ops[opsa[i]] = true;
  2564.         }
  2565.         for (i = 0; i < opsmatcha.length; i += 1) {
  2566.             opsmatch[opsmatcha[i]] = true;
  2567.         }
  2568.         return function (ch, matchattr) {
  2569.             if (matchattr) {
  2570.                 return !!opsmatch[ch];
  2571.             }
  2572.             return !!ops[ch];
  2573.         };
  2574.     }());
  2575.     
  2576.     // shorthands
  2577.     function isset(v) {
  2578.         return typeof v !== 'undefined';
  2579.     }
  2580.     function getConf() {
  2581.         return {
  2582.             'char': walker.chnum,
  2583.             line: walker.linenum
  2584.         };
  2585.     }
  2586.  
  2587.  
  2588.     // creates token objects and pushes them to a list
  2589.     function tokener(value, type, conf) {
  2590.         var w = walker, c = conf || {};
  2591.         tokens.push({
  2592.             charstart: isset(c['char']) ? c['char'] : w.chnum,
  2593.             charend:   isset(c.charend) ? c.charend : w.chnum,
  2594.             linestart: isset(c.line)    ? c.line    : w.linenum,
  2595.             lineend:   isset(c.lineend) ? c.lineend : w.linenum,
  2596.             value:     value,
  2597.             type:      type || value
  2598.         });
  2599.     }
  2600.     
  2601.     // oops
  2602.     function error(m, config) { 
  2603.         var w = walker,
  2604.             conf = config || {},
  2605.             c = isset(conf['char']) ? conf['char'] : w.chnum,
  2606.             l = isset(conf.line) ? conf.line : w.linenum;
  2607.         return {
  2608.             name: "ParseError",
  2609.             message: m + " at line " + (l + 1) + ' char ' + (c + 1),
  2610.             walker: w,
  2611.             tokens: tokens
  2612.         };
  2613.     }
  2614.  
  2615.  
  2616.     // token handlers follow for:
  2617.     // white space, comment, string, identifier, number, operator
  2618.     function white() {
  2619.     
  2620.         var c = walker.ch,
  2621.             token = '',
  2622.             conf = getConf();
  2623.     
  2624.         while (c === " " || c === "\t") {
  2625.             token += c;
  2626.             c = walker.nextChar();
  2627.         }
  2628.     
  2629.         tokener(token, 'white', conf);
  2630.     
  2631.     }
  2632.  
  2633.     function comment() {
  2634.     
  2635.         var w = walker,
  2636.             c = w.ch,
  2637.             token = c,
  2638.             cnext,
  2639.             conf = getConf();    
  2640.      
  2641.         cnext = w.nextChar();
  2642.         
  2643.         if (cnext !== '*') {
  2644.             // oops, not a comment, just a /
  2645.             conf.charend = conf['char'];
  2646.             conf.lineend = conf.line;
  2647.             return tokener(token, token, conf);
  2648.         }
  2649.     
  2650.         while (!(c === "*" && cnext === "/")) {
  2651.             token += cnext;
  2652.             c = cnext;
  2653.             cnext = w.nextChar();        
  2654.         }
  2655.         token += cnext;
  2656.         w.nextChar();
  2657.         tokener(token, 'comment', conf);
  2658.     }
  2659.  
  2660.     function str() {
  2661.         var w = walker,
  2662.             c = w.ch,
  2663.             q = c,
  2664.             token = c,
  2665.             cnext,
  2666.             conf = getConf();
  2667.     
  2668.         c = w.nextChar();
  2669.     
  2670.         while (c !== q) {
  2671.             
  2672.             if (c === '\n') {
  2673.                 cnext = w.nextChar();
  2674.                 if (cnext === "\\") {
  2675.                     token += c + cnext;
  2676.                 } else {
  2677.                     // end of line with no \ escape = bad
  2678.                     throw error("Unterminated string", conf);
  2679.                 }
  2680.             } else {
  2681.                 if (c === "\\") {
  2682.                     token += c + w.nextChar();
  2683.                 } else {
  2684.                     token += c;
  2685.                 }
  2686.             }
  2687.         
  2688.             c = w.nextChar();
  2689.         
  2690.         }
  2691.         token += c;
  2692.         w.nextChar();
  2693.         tokener(token, 'string', conf);
  2694.     }
  2695.     
  2696.     function brace() {
  2697.         var w = walker,
  2698.             c = w.ch,
  2699.             depth = 0,
  2700.             token = c,
  2701.             conf = getConf();
  2702.     
  2703.         c = w.nextChar();
  2704.     
  2705.         while (c !== ')' && !depth) {
  2706.             if (c === '(') {
  2707.                 depth++;
  2708.             } else if (c === ')') {
  2709.                 depth--;
  2710.             } else if (c === false) {
  2711.                 throw error("Unterminated brace", conf);
  2712.             }
  2713.             
  2714.             token += c;
  2715.             c = w.nextChar();
  2716.         }
  2717.         
  2718.         token += c;
  2719.         w.nextChar();
  2720.         tokener(token, 'brace', conf);
  2721.     }
  2722.  
  2723.     function identifier(pre) {
  2724.         var w = walker,
  2725.             c = w.ch,
  2726.             conf = getConf(),
  2727.             token = (pre) ? pre + c : c;
  2728.             
  2729.         c = w.nextChar();
  2730.     
  2731.         if (pre) { // adjust token position
  2732.             conf['char'] -= pre.length;
  2733.         }
  2734.         
  2735.         while (isNameChar(c) || isDigit(c)) {
  2736.             token += c;
  2737.             c = w.nextChar();
  2738.         }
  2739.     
  2740.         tokener(token, 'identifier', conf);    
  2741.     }
  2742.  
  2743.     function num() {
  2744.         var w = walker,
  2745.             c = w.ch,
  2746.             conf = getConf(),
  2747.             token = c,
  2748.             point = token === '.',
  2749.             nondigit;
  2750.         
  2751.         c = w.nextChar();
  2752.         nondigit = !isDigit(c);
  2753.     
  2754.         // .2px or .classname?
  2755.         if (point && nondigit) {
  2756.             // meh, NaN, could be a class name, so it's an operator for now
  2757.             conf.charend = conf['char'];
  2758.             conf.lineend = conf.line;
  2759.             return tokener(token, '.', conf);    
  2760.         }
  2761.         
  2762.         // -2px or -moz-something
  2763.         if (token === '-' && nondigit) {
  2764.             return identifier('-');
  2765.         }
  2766.     
  2767.         while (c !== false && (isDigit(c) || (!point && c === '.'))) { // not end of source && digit or first instance of .
  2768.             if (c === '.') {
  2769.                 point = true;
  2770.             }
  2771.             token += c;
  2772.             c = w.nextChar();
  2773.         }
  2774.  
  2775.         tokener(token, 'number', conf);    
  2776.     
  2777.     }
  2778.  
  2779.     function op() {
  2780.         var w = walker,
  2781.             c = w.ch,
  2782.             conf = getConf(),
  2783.             token = c,
  2784.             next = w.nextChar();
  2785.             
  2786.         if (next === "=" && isOp(token, true)) {
  2787.             token += next;
  2788.             tokener(token, 'match', conf);
  2789.             w.nextChar();
  2790.             return;
  2791.         } 
  2792.         
  2793.         conf.charend = conf['char'] + 1;
  2794.         conf.lineend = conf.line;    
  2795.         tokener(token, token, conf);
  2796.     }
  2797.  
  2798.  
  2799.     // call the appropriate handler based on the first character in a token suspect
  2800.     function tokenize() {
  2801.  
  2802.         var ch = walker.ch;
  2803.     
  2804.         if (ch === " " || ch === "\t") {
  2805.             return white();
  2806.         }
  2807.  
  2808.         if (ch === '/') {
  2809.             return comment();
  2810.         } 
  2811.  
  2812.         if (ch === '"' || ch === "'") {
  2813.             return str();
  2814.         }
  2815.         
  2816.         if (ch === '(') {
  2817.             return brace();
  2818.         }
  2819.     
  2820.         if (ch === '-' || ch === '.' || isDigit(ch)) { // tricky - char: minus (-1px) or dash (-moz-stuff)
  2821.             return num();
  2822.         }
  2823.     
  2824.         if (isNameChar(ch)) {
  2825.             return identifier();
  2826.         }
  2827.  
  2828.         if (isOp(ch)) {
  2829.             return op();
  2830.         }
  2831.         
  2832.         if (ch === "\n") {
  2833.             tokener("line");
  2834.             walker.nextChar();
  2835.             return;
  2836.         }
  2837.         
  2838.         throw error("Unrecognized character");
  2839.     }
  2840.     
  2841.     /**
  2842.      * Returns newline character at specified position in content
  2843.      * @param {String} content
  2844.      * @param {Number} pos
  2845.      * @return {String}
  2846.      */
  2847.     function getNewline(content, pos) {
  2848.         return content.charAt(pos) == '\r' && content.charAt(pos + 1) == '\n' 
  2849.             ? '\r\n' 
  2850.             : content.charAt(pos);
  2851.     }
  2852.  
  2853.     return {
  2854.         /**
  2855.          * @param source
  2856.          * @returns
  2857.          * @memberOf emmet.cssParser
  2858.          */
  2859.         lex: function (source) {
  2860.             walker.init(source);
  2861.             tokens = [];
  2862.             while (walker.ch !== false) {
  2863.                 tokenize();            
  2864.             }
  2865.             return tokens;
  2866.         },
  2867.         
  2868.         /**
  2869.          * Tokenizes CSS source
  2870.          * @param {String} source
  2871.          * @returns {Array}
  2872.          */
  2873.         parse: function(source) {
  2874.             // transform tokens
  2875.              var pos = 0;
  2876.              return _.map(this.lex(source), function(token) {
  2877.                  if (token.type == 'line') {
  2878.                      token.value = getNewline(source, pos);
  2879.                  }
  2880.                  
  2881.                  return {
  2882.                      type: token.type,
  2883.                      start: pos,
  2884.                      end: (pos += token.value.length)
  2885.                  };
  2886.             });
  2887.         },
  2888.         
  2889.         toSource: function (toks) {
  2890.             var i = 0, max = toks.length, t, src = '';
  2891.             for (; i < max; i += 1) {
  2892.                 t = toks[i];
  2893.                 if (t.type === 'line') {
  2894.                     src += '\n';
  2895.                 } else {
  2896.                     src += t.value;
  2897.                 }
  2898.             }
  2899.             return src;
  2900.         }
  2901.     };
  2902. });/**
  2903.  * HTML tokenizer by Marijn Haverbeke
  2904.  * http://codemirror.net/
  2905.  * @constructor
  2906.  * @memberOf __xmlParseDefine
  2907.  * @param {Function} require
  2908.  * @param {Underscore} _
  2909.  */
  2910. emmet.define('xmlParser', function(require, _) {
  2911.     var Kludges = {
  2912.         autoSelfClosers : {},
  2913.         implicitlyClosed : {},
  2914.         contextGrabbers : {},
  2915.         doNotIndent : {},
  2916.         allowUnquoted : true,
  2917.         allowMissing : true
  2918.     };
  2919.  
  2920.     // Return variables for tokenizers
  2921.     var tagName = null, type = null;
  2922.  
  2923.     function inText(stream, state) {
  2924.         function chain(parser) {
  2925.             state.tokenize = parser;
  2926.             return parser(stream, state);
  2927.         }
  2928.  
  2929.         var ch = stream.next();
  2930.         if (ch == "<") {
  2931.             if (stream.eat("!")) {
  2932.                 if (stream.eat("[")) {
  2933.                     if (stream.match("CDATA["))
  2934.                         return chain(inBlock("atom", "]]>"));
  2935.                     else
  2936.                         return null;
  2937.                 } else if (stream.match("--"))
  2938.                     return chain(inBlock("comment", "-->"));
  2939.                 else if (stream.match("DOCTYPE", true, true)) {
  2940.                     stream.eatWhile(/[\w\._\-]/);
  2941.                     return chain(doctype(1));
  2942.                 } else
  2943.                     return null;
  2944.             } else if (stream.eat("?")) {
  2945.                 stream.eatWhile(/[\w\._\-]/);
  2946.                 state.tokenize = inBlock("meta", "?>");
  2947.                 return "meta";
  2948.             } else {
  2949.                 type = stream.eat("/") ? "closeTag" : "openTag";
  2950.                 stream.eatSpace();
  2951.                 tagName = "";
  2952.                 var c;
  2953.                 while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/)))
  2954.                     tagName += c;
  2955.                 state.tokenize = inTag;
  2956.                 return "tag";
  2957.             }
  2958.         } else if (ch == "&") {
  2959.             var ok;
  2960.             if (stream.eat("#")) {
  2961.                 if (stream.eat("x")) {
  2962.                     ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
  2963.                 } else {
  2964.                     ok = stream.eatWhile(/[\d]/) && stream.eat(";");
  2965.                 }
  2966.             } else {
  2967.                 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
  2968.             }
  2969.             return ok ? "atom" : "error";
  2970.         } else {
  2971.             stream.eatWhile(/[^&<]/);
  2972.             return "text";
  2973.         }
  2974.     }
  2975.  
  2976.     function inTag(stream, state) {
  2977.         var ch = stream.next();
  2978.         if (ch == ">" || (ch == "/" && stream.eat(">"))) {
  2979.             state.tokenize = inText;
  2980.             type = ch == ">" ? "endTag" : "selfcloseTag";
  2981.             return "tag";
  2982.         } else if (ch == "=") {
  2983.             type = "equals";
  2984.             return null;
  2985.         } else if (/[\'\"]/.test(ch)) {
  2986.             state.tokenize = inAttribute(ch);
  2987.             return state.tokenize(stream, state);
  2988.         } else {
  2989.             stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
  2990.             return "word";
  2991.         }
  2992.     }
  2993.  
  2994.     function inAttribute(quote) {
  2995.         return function(stream, state) {
  2996.             while (!stream.eol()) {
  2997.                 if (stream.next() == quote) {
  2998.                     state.tokenize = inTag;
  2999.                     break;
  3000.                 }
  3001.             }
  3002.             return "string";
  3003.         };
  3004.     }
  3005.  
  3006.     function inBlock(style, terminator) {
  3007.         return function(stream, state) {
  3008.             while (!stream.eol()) {
  3009.                 if (stream.match(terminator)) {
  3010.                     state.tokenize = inText;
  3011.                     break;
  3012.                 }
  3013.                 stream.next();
  3014.             }
  3015.             return style;
  3016.         };
  3017.     }
  3018.     
  3019.     function doctype(depth) {
  3020.         return function(stream, state) {
  3021.             var ch;
  3022.             while ((ch = stream.next()) != null) {
  3023.                 if (ch == "<") {
  3024.                     state.tokenize = doctype(depth + 1);
  3025.                     return state.tokenize(stream, state);
  3026.                 } else if (ch == ">") {
  3027.                     if (depth == 1) {
  3028.                         state.tokenize = inText;
  3029.                         break;
  3030.                     } else {
  3031.                         state.tokenize = doctype(depth - 1);
  3032.                         return state.tokenize(stream, state);
  3033.                     }
  3034.                 }
  3035.             }
  3036.             return "meta";
  3037.         };
  3038.     }
  3039.  
  3040.     var curState = null, setStyle;
  3041.     function pass() {
  3042.         for (var i = arguments.length - 1; i >= 0; i--)
  3043.             curState.cc.push(arguments[i]);
  3044.     }
  3045.     
  3046.     function cont() {
  3047.         pass.apply(null, arguments);
  3048.         return true;
  3049.     }
  3050.  
  3051.     function pushContext(tagName, startOfLine) {
  3052.         var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) 
  3053.             || (curState.context && curState.context.noIndent);
  3054.         curState.context = {
  3055.             prev : curState.context,
  3056.             tagName : tagName,
  3057.             indent : curState.indented,
  3058.             startOfLine : startOfLine,
  3059.             noIndent : noIndent
  3060.         };
  3061.     }
  3062.     
  3063.     function popContext() {
  3064.         if (curState.context)
  3065.             curState.context = curState.context.prev;
  3066.     }
  3067.  
  3068.     function element(type) {
  3069.         if (type == "openTag") {
  3070.             curState.tagName = tagName;
  3071.             return cont(attributes, endtag(curState.startOfLine));
  3072.         } else if (type == "closeTag") {
  3073.             var err = false;
  3074.             if (curState.context) {
  3075.                 if (curState.context.tagName != tagName) {
  3076.                     if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
  3077.                         popContext();
  3078.                     }
  3079.                     err = !curState.context || curState.context.tagName != tagName;
  3080.                 }
  3081.             } else {
  3082.                 err = true;
  3083.             }
  3084.             
  3085.             if (err)
  3086.                 setStyle = "error";
  3087.             return cont(endclosetag(err));
  3088.         }
  3089.         return cont();
  3090.     }
  3091.     
  3092.     function endtag(startOfLine) {
  3093.         return function(type) {
  3094.             if (type == "selfcloseTag"
  3095.                     || (type == "endTag" && Kludges.autoSelfClosers
  3096.                             .hasOwnProperty(curState.tagName
  3097.                                     .toLowerCase()))) {
  3098.                 maybePopContext(curState.tagName.toLowerCase());
  3099.                 return cont();
  3100.             }
  3101.             if (type == "endTag") {
  3102.                 maybePopContext(curState.tagName.toLowerCase());
  3103.                 pushContext(curState.tagName, startOfLine);
  3104.                 return cont();
  3105.             }
  3106.             return cont();
  3107.         };
  3108.     }
  3109.     
  3110.     function endclosetag(err) {
  3111.         return function(type) {
  3112.             if (err)
  3113.                 setStyle = "error";
  3114.             if (type == "endTag") {
  3115.                 popContext();
  3116.                 return cont();
  3117.             }
  3118.             setStyle = "error";
  3119.             return cont(arguments.callee);
  3120.         };
  3121.     }
  3122.     
  3123.     function maybePopContext(nextTagName) {
  3124.         var parentTagName;
  3125.         while (true) {
  3126.             if (!curState.context) {
  3127.                 return;
  3128.             }
  3129.             parentTagName = curState.context.tagName.toLowerCase();
  3130.             if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName)
  3131.                     || !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
  3132.                 return;
  3133.             }
  3134.             popContext();
  3135.         }
  3136.     }
  3137.  
  3138.     function attributes(type) {
  3139.         if (type == "word") {
  3140.             setStyle = "attribute";
  3141.             return cont(attribute, attributes);
  3142.         }
  3143.         if (type == "endTag" || type == "selfcloseTag")
  3144.             return pass();
  3145.         setStyle = "error";
  3146.         return cont(attributes);
  3147.     }
  3148.     
  3149.     function attribute(type) {
  3150.         if (type == "equals")
  3151.             return cont(attvalue, attributes);
  3152.         if (!Kludges.allowMissing)
  3153.             setStyle = "error";
  3154.         return (type == "endTag" || type == "selfcloseTag") ? pass()
  3155.                 : cont();
  3156.     }
  3157.     
  3158.     function attvalue(type) {
  3159.         if (type == "string")
  3160.             return cont(attvaluemaybe);
  3161.         if (type == "word" && Kludges.allowUnquoted) {
  3162.             setStyle = "string";
  3163.             return cont();
  3164.         }
  3165.         setStyle = "error";
  3166.         return (type == "endTag" || type == "selfCloseTag") ? pass()
  3167.                 : cont();
  3168.     }
  3169.     
  3170.     function attvaluemaybe(type) {
  3171.         if (type == "string")
  3172.             return cont(attvaluemaybe);
  3173.         else
  3174.             return pass();
  3175.     }
  3176.     
  3177.     function startState() {
  3178.         return {
  3179.             tokenize : inText,
  3180.             cc : [],
  3181.             indented : 0,
  3182.             startOfLine : true,
  3183.             tagName : null,
  3184.             context : null
  3185.         };
  3186.     }
  3187.     
  3188.     function token(stream, state) {
  3189.         if (stream.sol()) {
  3190.             state.startOfLine = true;
  3191.             state.indented = 0;
  3192.         }
  3193.         
  3194.         if (stream.eatSpace())
  3195.             return null;
  3196.  
  3197.         setStyle = type = tagName = null;
  3198.         var style = state.tokenize(stream, state);
  3199.         state.type = type;
  3200.         if ((style || type) && style != "comment") {
  3201.             curState = state;
  3202.             while (true) {
  3203.                 var comb = state.cc.pop() || element;
  3204.                 if (comb(type || style))
  3205.                     break;
  3206.             }
  3207.         }
  3208.         state.startOfLine = false;
  3209.         return setStyle || style;
  3210.     }
  3211.  
  3212.     return {
  3213.         /**
  3214.          * @memberOf emmet.xmlParser
  3215.          * @returns
  3216.          */
  3217.         parse: function(data, offset) {
  3218.             offset = offset || 0;
  3219.             var state = startState();
  3220.             var stream = require('stringStream').create(data);
  3221.             var tokens = [];
  3222.             while (!stream.eol()) {
  3223.                 tokens.push({
  3224.                     type: token(stream, state),
  3225.                     start: stream.start + offset,
  3226.                     end: stream.pos + offset
  3227.                 });
  3228.                 stream.start = stream.pos;
  3229.             }
  3230.             
  3231.             return tokens;
  3232.         }        
  3233.     };
  3234. });
  3235. /*!
  3236.  * string_score.js: String Scoring Algorithm 0.1.10 
  3237.  *
  3238.  * http://joshaven.com/string_score
  3239.  * https://github.com/joshaven/string_score
  3240.  *
  3241.  * Copyright (C) 2009-2011 Joshaven Potter <yourtech@gmail.com>
  3242.  * Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
  3243.  * MIT license: http://www.opensource.org/licenses/mit-license.php
  3244.  *
  3245.  * Date: Tue Mar 1 2011
  3246. */
  3247.  
  3248. /**
  3249.  * Scores a string against another string.
  3250.  *  'Hello World'.score('he');     //=> 0.5931818181818181
  3251.  *  'Hello World'.score('Hello');  //=> 0.7318181818181818
  3252.  */
  3253. emmet.define('string-score', function(require, _) {
  3254.     return {
  3255.         score: function(string, abbreviation, fuzziness) {
  3256.             // If the string is equal to the abbreviation, perfect match.
  3257.               if (string == abbreviation) {return 1;}
  3258.               //if it's not a perfect match and is empty return 0
  3259.               if(abbreviation == "") {return 0;}
  3260.  
  3261.               var total_character_score = 0,
  3262.                   abbreviation_length = abbreviation.length,
  3263.                   string_length = string.length,
  3264.                   start_of_string_bonus,
  3265.                   abbreviation_score,
  3266.                   fuzzies=1,
  3267.                   final_score;
  3268.               
  3269.               // Walk through abbreviation and add up scores.
  3270.               for (var i = 0,
  3271.                      character_score/* = 0*/,
  3272.                      index_in_string/* = 0*/,
  3273.                      c/* = ''*/,
  3274.                      index_c_lowercase/* = 0*/,
  3275.                      index_c_uppercase/* = 0*/,
  3276.                      min_index/* = 0*/;
  3277.                  i < abbreviation_length;
  3278.                  ++i) {
  3279.                 
  3280.                 // Find the first case-insensitive match of a character.
  3281.                 c = abbreviation.charAt(i);
  3282.                 
  3283.                 index_c_lowercase = string.indexOf(c.toLowerCase());
  3284.                 index_c_uppercase = string.indexOf(c.toUpperCase());
  3285.                 min_index = Math.min(index_c_lowercase, index_c_uppercase);
  3286.                 index_in_string = (min_index > -1) ? min_index : Math.max(index_c_lowercase, index_c_uppercase);
  3287.                 
  3288.                 if (index_in_string === -1) { 
  3289.                   if (fuzziness) {
  3290.                     fuzzies += 1-fuzziness;
  3291.                     continue;
  3292.                   } else {
  3293.                     return 0;
  3294.                   }
  3295.                 } else {
  3296.                   character_score = 0.1;
  3297.                 }
  3298.                 
  3299.                 // Set base score for matching 'c'.
  3300.                 
  3301.                 // Same case bonus.
  3302.                 if (string[index_in_string] === c) { 
  3303.                   character_score += 0.1; 
  3304.                 }
  3305.                 
  3306.                 // Consecutive letter & start-of-string Bonus
  3307.                 if (index_in_string === 0) {
  3308.                   // Increase the score when matching first character of the remainder of the string
  3309.                   character_score += 0.6;
  3310.                   if (i === 0) {
  3311.                     // If match is the first character of the string
  3312.                     // & the first character of abbreviation, add a
  3313.                     // start-of-string match bonus.
  3314.                     start_of_string_bonus = 1; //true;
  3315.                   }
  3316.                 }
  3317.                 else {
  3318.               // Acronym Bonus
  3319.               // Weighing Logic: Typing the first character of an acronym is as if you
  3320.               // preceded it with two perfect character matches.
  3321.               if (string.charAt(index_in_string - 1) === ' ') {
  3322.                 character_score += 0.8; // * Math.min(index_in_string, 5); // Cap bonus at 0.4 * 5
  3323.               }
  3324.                 }
  3325.                 
  3326.                 // Left trim the already matched part of the string
  3327.                 // (forces sequential matching).
  3328.                 string = string.substring(index_in_string + 1, string_length);
  3329.                 
  3330.                 total_character_score += character_score;
  3331.               } // end of for loop
  3332.               
  3333.               // Uncomment to weigh smaller words higher.
  3334.               // return total_character_score / string_length;
  3335.               
  3336.               abbreviation_score = total_character_score / abbreviation_length;
  3337.               //percentage_of_matched_string = abbreviation_length / string_length;
  3338.               //word_score = abbreviation_score * percentage_of_matched_string;
  3339.               
  3340.               // Reduce penalty for longer strings.
  3341.               //final_score = (word_score + abbreviation_score) / 2;
  3342.               final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2;
  3343.               
  3344.               final_score = final_score / fuzzies;
  3345.               
  3346.               if (start_of_string_bonus && (final_score + 0.15 < 1)) {
  3347.                 final_score += 0.15;
  3348.               }
  3349.               
  3350.               return final_score;
  3351.         }
  3352.     };
  3353. });/**
  3354.  * Utility module for Emmet
  3355.  * @param {Function} require
  3356.  * @param {Underscore} _
  3357.  */
  3358. emmet.define('utils', function(require, _) {
  3359.     /** 
  3360.      * Special token used as a placeholder for caret positions inside 
  3361.      * generated output 
  3362.      */
  3363.     var caretPlaceholder = '${0}';
  3364.     
  3365.     /**
  3366.      * A simple string builder, optimized for faster text concatenation
  3367.      * @param {String} value Initial value
  3368.      */
  3369.     function StringBuilder(value) {
  3370.         this._data = [];
  3371.         this.length = 0;
  3372.         
  3373.         if (value)
  3374.             this.append(value);
  3375.     }
  3376.     
  3377.     StringBuilder.prototype = {
  3378.         /**
  3379.          * Append string
  3380.          * @param {String} text
  3381.          */
  3382.         append: function(text) {
  3383.             this._data.push(text);
  3384.             this.length += text.length;
  3385.         },
  3386.         
  3387.         /**
  3388.          * @returns {String}
  3389.          */
  3390.         toString: function() {
  3391.             return this._data.join('');
  3392.         },
  3393.         
  3394.         /**
  3395.          * @returns {String}
  3396.          */
  3397.         valueOf: function() {
  3398.             return this.toString();
  3399.         }
  3400.     };
  3401.     
  3402.     return {
  3403.         /** @memberOf utils */
  3404.         reTag: /<\/?[\w:\-]+(?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*\s*(\/?)>$/,
  3405.         
  3406.         /**
  3407.          * Test if passed string ends with XHTML tag. This method is used for testing
  3408.          * '>' character: it belongs to tag or it's a part of abbreviation? 
  3409.          * @param {String} str
  3410.          * @return {Boolean}
  3411.          */
  3412.         endsWithTag: function(str) {
  3413.             return this.reTag.test(str);
  3414.         },
  3415.         
  3416.         /**
  3417.          * Check if passed symbol is a number
  3418.          * @param {String} ch
  3419.          * @returns {Boolean}
  3420.          */
  3421.         isNumeric: function(ch) {
  3422.             if (typeof(ch) == 'string')
  3423.                 ch = ch.charCodeAt(0);
  3424.                 
  3425.             return (ch && ch > 47 && ch < 58);
  3426.         },
  3427.         
  3428.         /**
  3429.          * Trim whitespace from string
  3430.          * @param {String} text
  3431.          * @return {String}
  3432.          */
  3433.         trim: function(text) {
  3434.             return (text || "").replace(/^\s+|\s+$/g, "");
  3435.         },
  3436.         
  3437.         /**
  3438.          * Returns newline character
  3439.          * @returns {String}
  3440.          */
  3441.         getNewline: function() {
  3442.             var res = require('resources');
  3443.             if (!res) {
  3444.                 return '\n';
  3445.             }
  3446.             
  3447.             var nl = res.getVariable('newline');
  3448.             return _.isString(nl) ? nl : '\n';
  3449.         },
  3450.         
  3451.         /**
  3452.          * Sets new newline character that will be used in output
  3453.          * @param {String} str
  3454.          */
  3455.         setNewline: function(str) {
  3456.             var res = require('resources');
  3457.             res.setVariable('newline', str);
  3458.             res.setVariable('nl', str);
  3459.         },
  3460.         
  3461.         /**
  3462.          * Split text into lines. Set <code>remove_empty</code> to true to filter
  3463.          * empty lines
  3464.          * @param {String} text Text to split
  3465.          * @param {Boolean} removeEmpty Remove empty lines from result
  3466.          * @return {Array}
  3467.          */
  3468.         splitByLines: function(text, removeEmpty) {
  3469.             // IE fails to split string by regexp, 
  3470.             // need to normalize newlines first
  3471.             // Also, Mozilla's Rhiho JS engine has a weird newline bug
  3472.             var nl = this.getNewline();
  3473.             var lines = (text || '')
  3474.                 .replace(/\r\n/g, '\n')
  3475.                 .replace(/\n\r/g, '\n')
  3476.                 .replace(/\r/g, '\n')
  3477.                 .replace(/\n/g, nl)
  3478.                 .split(nl);
  3479.             
  3480.             if (removeEmpty) {
  3481.                 lines = _.filter(lines, function(line) {
  3482.                     return line.length && !!this.trim(line);
  3483.                 }, this);
  3484.             }
  3485.             
  3486.             return lines;
  3487.         },
  3488.         
  3489.         /**
  3490.          * Normalizes newline character: replaces newlines in <code>text</code> 
  3491.          * with newline defined in preferences
  3492.          * @param {String} text
  3493.          * @returns {String}
  3494.          */
  3495.         normalizeNewline: function(text) {
  3496.             return this.splitByLines(text).join(this.getNewline());
  3497.         },
  3498.         
  3499.         /**
  3500.          * Repeats string <code>howMany</code> times
  3501.          * @param {String} str
  3502.          * @param {Number} how_many
  3503.          * @return {String}
  3504.          */
  3505.         repeatString: function(str, howMany) {
  3506.             var result = [];
  3507.             
  3508.             for (var i = 0; i < howMany; i++) 
  3509.                 result.push(str);
  3510.                 
  3511.             return result.join('');
  3512.         },
  3513.         
  3514.         /**
  3515.          * Indents text with padding
  3516.          * @param {String} text Text to indent
  3517.          * @param {String} pad Padding size (number) or padding itself (string)
  3518.          * @return {String}
  3519.          */
  3520.         padString: function(text, pad) {
  3521.             var padStr = (_.isNumber(pad)) 
  3522.                 ? this.repeatString(require('resources').getVariable('indentation') || '\t', pad) 
  3523.                 : pad;
  3524.                 
  3525.             var result = [];
  3526.             
  3527.             var lines = this.splitByLines(text);
  3528.             var nl = this.getNewline();
  3529.                 
  3530.             result.push(lines[0]);
  3531.             for (var j = 1; j < lines.length; j++) 
  3532.                 result.push(nl + padStr + lines[j]);
  3533.                 
  3534.             return result.join('');
  3535.         },
  3536.         
  3537.         /**
  3538.          * Pad string with zeroes
  3539.          * @param {String} str String to pad
  3540.          * @param {Number} pad Desired string length
  3541.          * @return {String}
  3542.          */
  3543.         zeroPadString: function(str, pad) {
  3544.             var padding = '';
  3545.             var il = str.length;
  3546.                 
  3547.             while (pad > il++) padding += '0';
  3548.             return padding + str; 
  3549.         },
  3550.         
  3551.         /**
  3552.          * Removes padding at the beginning of each text's line
  3553.          * @param {String} text
  3554.          * @param {String} pad
  3555.          */
  3556.         unindentString: function(text, pad) {
  3557.             var lines = this.splitByLines(text);
  3558.             for (var i = 0; i < lines.length; i++) {
  3559.                 if (lines[i].search(pad) == 0)
  3560.                     lines[i] = lines[i].substr(pad.length);
  3561.             }
  3562.             
  3563.             return lines.join(this.getNewline());
  3564.         },
  3565.         
  3566.         /**
  3567.          * Replaces unescaped symbols in <code>str</code>. For example, the '$' symbol
  3568.          * will be replaced in 'item$count', but not in 'item\$count'.
  3569.          * @param {String} str Original string
  3570.          * @param {String} symbol Symbol to replace
  3571.          * @param {String} replace Symbol replacement. Might be a function that 
  3572.          * returns new value
  3573.          * @return {String}
  3574.          */
  3575.         replaceUnescapedSymbol: function(str, symbol, replace) {
  3576.             var i = 0;
  3577.             var il = str.length;
  3578.             var sl = symbol.length;
  3579.             var matchCount = 0;
  3580.                 
  3581.             while (i < il) {
  3582.                 if (str.charAt(i) == '\\') {
  3583.                     // escaped symbol, skip next character
  3584.                     i += sl + 1;
  3585.                 } else if (str.substr(i, sl) == symbol) {
  3586.                     // have match
  3587.                     var curSl = sl;
  3588.                     matchCount++;
  3589.                     var newValue = replace;
  3590.                     if (_.isFunction(replace)) {
  3591.                         var replaceData = replace(str, symbol, i, matchCount);
  3592.                         if (replaceData) {
  3593.                             curSl = replaceData[0].length;
  3594.                             newValue = replaceData[1];
  3595.                         } else {
  3596.                             newValue = false;
  3597.                         }
  3598.                     }
  3599.                     
  3600.                     if (newValue === false) { // skip replacement
  3601.                         i++;
  3602.                         continue;
  3603.                     }
  3604.                     
  3605.                     str = str.substring(0, i) + newValue + str.substring(i + curSl);
  3606.                     // adjust indexes
  3607.                     il = str.length;
  3608.                     i += newValue.length;
  3609.                 } else {
  3610.                     i++;
  3611.                 }
  3612.             }
  3613.             
  3614.             return str;
  3615.         },
  3616.         
  3617.         /**
  3618.          * Replace variables like ${var} in string
  3619.          * @param {String} str
  3620.          * @param {Object} vars Variable set (defaults to variables defined in 
  3621.          * <code>snippets.json</code>) or variable resolver (<code>Function</code>)
  3622.          * @return {String}
  3623.          */
  3624.         replaceVariables: function(str, vars) {
  3625.             vars = vars || {};
  3626.             var resolver = _.isFunction(vars) ? vars : function(str, p1) {
  3627.                 return p1 in vars ? vars[p1] : null;
  3628.             };
  3629.             
  3630.             var res = require('resources');
  3631.             return require('tabStops').processText(str, {
  3632.                 variable: function(data) {
  3633.                     var newValue = resolver(data.token, data.name, data);
  3634.                     if (newValue === null) {
  3635.                         // try to find variable in resources
  3636.                         newValue = res.getVariable(data.name);
  3637.                     }
  3638.                     
  3639.                     if (newValue === null || _.isUndefined(newValue))
  3640.                         // nothing found, return token itself
  3641.                         newValue = data.token;
  3642.                     return newValue;
  3643.                 }
  3644.             });
  3645.         },
  3646.         
  3647.         /**
  3648.          * Replaces '$' character in string assuming it might be escaped with '\'
  3649.          * @param {String} str String where caracter should be replaced
  3650.          * @param {String} value Replace value. Might be a <code>Function</code>
  3651.          * @return {String}
  3652.          */
  3653.         replaceCounter: function(str, value) {
  3654.             var symbol = '$';
  3655.             // in case we received strings from Java, convert the to native strings
  3656.             str = String(str);
  3657.             value = String(value);
  3658.             var that = this;
  3659.             return this.replaceUnescapedSymbol(str, symbol, function(str, symbol, pos, matchNum){
  3660.                 if (str.charAt(pos + 1) == '{' || that.isNumeric(str.charAt(pos + 1)) ) {
  3661.                     // it's a variable, skip it
  3662.                     return false;
  3663.                 }
  3664.                 
  3665.                 // replace sequense of $ symbols with padded number  
  3666.                 var j = pos + 1;
  3667.                 while(str.charAt(j) == '$' && str.charAt(j + 1) != '{') j++;
  3668.                 return [str.substring(pos, j), that.zeroPadString(value, j - pos)];
  3669.             });
  3670.         },
  3671.         
  3672.         /**
  3673.          * Check if string matches against <code>reTag</code> regexp. This 
  3674.          * function may be used to test if provided string contains HTML tags
  3675.          * @param {String} str
  3676.          * @returns {Boolean}
  3677.          */
  3678.         matchesTag: function(str) {
  3679.             return this.reTag.test(str || '');
  3680.         },
  3681.         
  3682.         /**
  3683.          * Escapes special characters used in Emmet, like '$', '|', etc.
  3684.          * Use this method before passing to actions like "Wrap with Abbreviation"
  3685.          * to make sure that existing special characters won't be altered
  3686.          * @param {String} text
  3687.          * @return {String}
  3688.          */
  3689.         escapeText: function(text) {
  3690.             return text.replace(/([\$\\])/g, '\\$1');
  3691.         },
  3692.         
  3693.         /**
  3694.          * Unescapes special characters used in Emmet, like '$', '|', etc.
  3695.          * @param {String} text
  3696.          * @return {String}
  3697.          */
  3698.         unescapeText: function(text) {
  3699.             return text.replace(/\\(.)/g, '$1');
  3700.         },
  3701.         
  3702.         /**
  3703.          * Returns caret placeholder
  3704.          * @returns {String}
  3705.          */
  3706.         getCaretPlaceholder: function() {
  3707.             return _.isFunction(caretPlaceholder) 
  3708.                 ? caretPlaceholder.apply(this, arguments)
  3709.                 : caretPlaceholder;
  3710.         },
  3711.         
  3712.         /**
  3713.          * Sets new representation for carets in generated output
  3714.          * @param {String} value New caret placeholder. Might be a 
  3715.          * <code>Function</code>
  3716.          */
  3717.         setCaretPlaceholder: function(value) {
  3718.             caretPlaceholder = value;
  3719.         },
  3720.         
  3721.         /**
  3722.          * Returns line padding
  3723.          * @param {String} line
  3724.          * @return {String}
  3725.          */
  3726.         getLinePadding: function(line) {
  3727.             return (line.match(/^(\s+)/) || [''])[0];
  3728.         },
  3729.         
  3730.         /**
  3731.          * Helper function that returns padding of line of <code>pos</code>
  3732.          * position in <code>content</code>
  3733.          * @param {String} content
  3734.          * @param {Number} pos
  3735.          * @returns {String}
  3736.          */
  3737.         getLinePaddingFromPosition: function(content, pos) {
  3738.             var lineRange = this.findNewlineBounds(content, pos);
  3739.             return this.getLinePadding(lineRange.substring(content));
  3740.         },
  3741.         
  3742.         /**
  3743.          * Escape special regexp chars in string, making it usable for creating dynamic
  3744.          * regular expressions
  3745.          * @param {String} str
  3746.          * @return {String}
  3747.          */
  3748.         escapeForRegexp: function(str) {
  3749.             var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
  3750.             return str.replace(specials, "\\$&");
  3751.         },
  3752.         
  3753.         /**
  3754.          * Make decimal number look good: convert it to fixed precision end remove
  3755.          * traling zeroes 
  3756.          * @param {Number} num
  3757.          * @param {Number} fracion Fraction numbers (default is 2)
  3758.          * @return {String}
  3759.          */
  3760.         prettifyNumber: function(num, fraction) {
  3761.             return num.toFixed(typeof fraction == 'undefined' ? 2 : fraction).replace(/\.?0+$/, '');
  3762.         },
  3763.         
  3764.         /**
  3765.          * A simple mutable string shim, optimized for faster text concatenation
  3766.          * @param {String} value Initial value
  3767.          * @returns {StringBuilder}
  3768.          */
  3769.         stringBuilder: function(value) {
  3770.             return new StringBuilder(value);
  3771.         },
  3772.         
  3773.         /**
  3774.          * Replace substring of <code>str</code> with <code>value</code>
  3775.          * @param {String} str String where to replace substring
  3776.          * @param {String} value New substring value
  3777.          * @param {Number} start Start index of substring to replace. May also
  3778.          * be a <code>Range</code> object: in this case, the <code>end</code>
  3779.          * argument is not required
  3780.          * @param {Number} end End index of substring to replace. If ommited, 
  3781.          * <code>start</code> argument is used
  3782.          */
  3783.         replaceSubstring: function(str, value, start, end) {
  3784.             if (_.isObject(start) && 'end' in start) {
  3785.                 end = start.end;
  3786.                 start = start.start;
  3787.             }
  3788.             
  3789.             if (_.isString(end))
  3790.                 end = start + end.length;
  3791.             
  3792.             if (_.isUndefined(end))
  3793.                 end = start;
  3794.             
  3795.             if (start < 0 || start > str.length)
  3796.                 return str;
  3797.             
  3798.             return str.substring(0, start) + value + str.substring(end);
  3799.         },
  3800.         
  3801.         /**
  3802.          * Narrows down text range, adjusting selection to non-space characters
  3803.          * @param {String} text
  3804.          * @param {Number} start Starting range in <code>text</code> where 
  3805.          * slection should be adjusted. Can also be any object that is accepted
  3806.          * by <code>Range</code> class
  3807.          * @return {Range}
  3808.          */
  3809.         narrowToNonSpace: function(text, start, end) {
  3810.             var range = require('range').create(start, end);
  3811.             
  3812.             var reSpace = /[\s\n\r\u00a0]/;
  3813.             // narrow down selection until first non-space character
  3814.             while (range.start < range.end) {
  3815.                 if (!reSpace.test(text.charAt(range.start)))
  3816.                     break;
  3817.                     
  3818.                 range.start++;
  3819.             }
  3820.             
  3821.             while (range.end > range.start) {
  3822.                 range.end--;
  3823.                 if (!reSpace.test(text.charAt(range.end))) {
  3824.                     range.end++;
  3825.                     break;
  3826.                 }
  3827.             }
  3828.             
  3829.             return range;
  3830.         },
  3831.         
  3832.         /**
  3833.          * Find start and end index of text line for <code>from</code> index
  3834.          * @param {String} text 
  3835.          * @param {Number} from
  3836.          */
  3837.         findNewlineBounds: function(text, from) {
  3838.             var len = text.length,
  3839.                 start = 0,
  3840.                 end = len - 1;
  3841.             
  3842.             // search left
  3843.             for (var i = from - 1; i > 0; i--) {
  3844.                 var ch = text.charAt(i);
  3845.                 if (ch == '\n' || ch == '\r') {
  3846.                     start = i + 1;
  3847.                     break;
  3848.                 }
  3849.             }
  3850.             // search right
  3851.             for (var j = from; j < len; j++) {
  3852.                 var ch = text.charAt(j);
  3853.                 if (ch == '\n' || ch == '\r') {
  3854.                     end = j;
  3855.                     break;
  3856.                 }
  3857.             }
  3858.             
  3859.             return require('range').create(start, end - start);
  3860.         },
  3861.  
  3862.         /**
  3863.          * Deep merge of two or more objects. Taken from jQuery.extend()
  3864.          */
  3865.         deepMerge: function() {
  3866.             var options, name, src, copy, copyIsArray, clone,
  3867.                 target = arguments[0] || {},
  3868.                 i = 1,
  3869.                 length = arguments.length;
  3870.  
  3871.  
  3872.             // Handle case when target is a string or something (possible in deep copy)
  3873.             if (!_.isObject(target) && !_.isFunction(target)) {
  3874.                 target = {};
  3875.             }
  3876.  
  3877.             for ( ; i < length; i++ ) {
  3878.                 // Only deal with non-null/undefined values
  3879.                 if ( (options = arguments[ i ]) != null ) {
  3880.                     // Extend the base object
  3881.                     for ( name in options ) {
  3882.                         src = target[ name ];
  3883.                         copy = options[ name ];
  3884.  
  3885.                         // Prevent never-ending loop
  3886.                         if ( target === copy ) {
  3887.                             continue;
  3888.                         }
  3889.  
  3890.                         // Recurse if we're merging plain objects or arrays
  3891.                         if ( copy && ( _.isObject(copy) || (copyIsArray = _.isArray(copy)) ) ) {
  3892.                             if ( copyIsArray ) {
  3893.                                 copyIsArray = false;
  3894.                                 clone = src && _.isArray(src) ? src : [];
  3895.  
  3896.                             } else {
  3897.                                 clone = src && _.isObject(src) ? src : {};
  3898.                             }
  3899.  
  3900.                             // Never move original objects, clone them
  3901.                             target[ name ] = this.deepMerge(clone, copy );
  3902.  
  3903.                         // Don't bring in undefined values
  3904.                         } else if ( copy !== undefined ) {
  3905.                             target[ name ] = copy;
  3906.                         }
  3907.                     }
  3908.                 }
  3909.             }
  3910.  
  3911.             // Return the modified object
  3912.             return target;
  3913.         }
  3914.     };
  3915. });
  3916. /**
  3917.  * Helper module to work with ranges
  3918.  * @constructor
  3919.  * @memberOf __rangeDefine
  3920.  * @param {Function} require
  3921.  * @param {Underscore} _
  3922.  */
  3923. emmet.define('range', function(require, _) {
  3924.     function cmp(a, b, op) {
  3925.         switch (op) {
  3926.             case 'eq':
  3927.             case '==':
  3928.                 return a === b;
  3929.             case 'lt':
  3930.             case '<':
  3931.                 return a < b;
  3932.             case 'lte':
  3933.             case '<=':
  3934.                 return a <= b;
  3935.             case 'gt':
  3936.             case '>':
  3937.                 return a > b;
  3938.             case 'gte':
  3939.             case '>=':
  3940.                 return a >= b;
  3941.         }
  3942.     }
  3943.     
  3944.     
  3945.     /**
  3946.      * @type Range
  3947.      * @constructor
  3948.      * @param {Object} start
  3949.      * @param {Number} len
  3950.      */
  3951.     function Range(start, len) {
  3952.         if (_.isObject(start) && 'start' in start) {
  3953.             // create range from object stub
  3954.             this.start = Math.min(start.start, start.end);
  3955.             this.end = Math.max(start.start, start.end);
  3956.         } else if (_.isArray(start)) {
  3957.             this.start = start[0];
  3958.             this.end = start[1];
  3959.         } else {
  3960.             len = _.isString(len) ? len.length : +len;
  3961.             this.start = start;
  3962.             this.end = start + len;
  3963.         }
  3964.     }
  3965.     
  3966.     Range.prototype = {
  3967.         length: function() {
  3968.             return Math.abs(this.end - this.start);
  3969.         },
  3970.         
  3971.         /**
  3972.          * Returns <code>true</code> if passed range is equals to current one
  3973.          * @param {Range} range
  3974.          * @returns {Boolean}
  3975.          */
  3976.         equal: function(range) {
  3977.             return this.cmp(range, 'eq', 'eq');
  3978. //            return this.start === range.start && this.end === range.end;
  3979.         },
  3980.         
  3981.         /**
  3982.          * Shifts indexes position with passed <code>delat</code>
  3983.          * @param {Number} delta
  3984.          * @returns {Range} range itself
  3985.          */
  3986.         shift: function(delta) {
  3987.             this.start += delta;
  3988.             this.end += delta;
  3989.             return this;
  3990.         },
  3991.         
  3992.         /**
  3993.          * Check if two ranges are overlapped
  3994.          * @param {Range} range
  3995.          * @returns {Boolean}
  3996.          */
  3997.         overlap: function(range) {
  3998.             return range.start <= this.end && range.end >= this.start;
  3999.         },
  4000.         
  4001.         /**
  4002.          * Finds intersection of two ranges
  4003.          * @param {Range} range
  4004.          * @returns {Range} <code>null</code> if ranges does not overlap
  4005.          */
  4006.         intersection: function(range) {
  4007.             if (this.overlap(range)) {
  4008.                 var start = Math.max(range.start, this.start);
  4009.                 var end = Math.min(range.end, this.end);
  4010.                 return new Range(start, end - start);
  4011.             }
  4012.             
  4013.             return null;
  4014.         },
  4015.         
  4016.         /**
  4017.          * Returns the union of the thow ranges.
  4018.          * @param {Range} range
  4019.          * @returns {Range} <code>null</code> if ranges are not overlapped
  4020.          */
  4021.         union: function(range) {
  4022.             if (this.overlap(range)) {
  4023.                 var start = Math.min(range.start, this.start);
  4024.                 var end = Math.max(range.end, this.end);
  4025.                 return new Range(start, end - start);
  4026.             }
  4027.             
  4028.             return null;
  4029.         },
  4030.         
  4031.         /**
  4032.          * Returns a Boolean value that indicates whether a specified position 
  4033.          * is in a given range.
  4034.          * @param {Number} loc
  4035.          */
  4036.         inside: function(loc) {
  4037.             return this.cmp(loc, 'lte', 'gt');
  4038. //            return this.start <= loc && this.end > loc;
  4039.         },
  4040.         
  4041.         /**
  4042.          * Returns a Boolean value that indicates whether a specified position 
  4043.          * is in a given range, but not equals bounds.
  4044.          * @param {Number} loc
  4045.          */
  4046.         contains: function(loc) {
  4047.             return this.cmp(loc, 'lt', 'gt');
  4048.         },
  4049.         
  4050.         /**
  4051.          * Check if current range completely includes specified one
  4052.          * @param {Range} r
  4053.          * @returns {Boolean} 
  4054.          */
  4055.         include: function(r) {
  4056.             return this.cmp(loc, 'lte', 'gte');
  4057. //            return this.start <= r.start && this.end >= r.end;
  4058.         },
  4059.         
  4060.         /**
  4061.          * Low-level comparision method
  4062.          * @param {Number} loc
  4063.          * @param {String} left Left comparison operator
  4064.          * @param {String} right Right comaprison operator
  4065.          */
  4066.         cmp: function(loc, left, right) {
  4067.             var a, b;
  4068.             if (loc instanceof Range) {
  4069.                 a = loc.start;
  4070.                 b = loc.end;
  4071.             } else {
  4072.                 a = b = loc;
  4073.             }
  4074.             
  4075.             return cmp(this.start, a, left || '<=') && cmp(this.end, b, right || '>');
  4076.         },
  4077.         
  4078.         /**
  4079.          * Returns substring of specified <code>str</code> for current range
  4080.          * @param {String} str
  4081.          * @returns {String}
  4082.          */
  4083.         substring: function(str) {
  4084.             return this.length() > 0 
  4085.                 ? str.substring(this.start, this.end) 
  4086.                 : '';
  4087.         },
  4088.         
  4089.         /**
  4090.          * Creates copy of current range
  4091.          * @returns {Range}
  4092.          */
  4093.         clone: function() {
  4094.             return new Range(this.start, this.length());
  4095.         },
  4096.         
  4097.         /**
  4098.          * @returns {Array}
  4099.          */
  4100.         toArray: function() {
  4101.             return [this.start, this.end];
  4102.         },
  4103.         
  4104.         toString: function() {
  4105.             return '{' + this.start + ', ' + this.length() + '}';
  4106.         }
  4107.     };
  4108.     
  4109.     return {
  4110.         /**
  4111.          * Creates new range object instance
  4112.          * @param {Object} start Range start or array with 'start' and 'end'
  4113.          * as two first indexes or object with 'start' and 'end' properties
  4114.          * @param {Number} len Range length or string to produce range from
  4115.          * @returns {Range}
  4116.          * @memberOf emmet.range
  4117.          */
  4118.         create: function(start, len) {
  4119.             if (_.isUndefined(start) || start === null)
  4120.                 return null;
  4121.             
  4122.             if (start instanceof Range)
  4123.                 return start;
  4124.             
  4125.             if (_.isObject(start) && 'start' in start && 'end' in start) {
  4126.                 len = start.end - start.start;
  4127.                 start = start.start;
  4128.             }
  4129.                 
  4130.             return new Range(start, len);
  4131.         },
  4132.         
  4133.         /**
  4134.          * <code>Range</code> object factory, the same as <code>this.create()</code>
  4135.          * but last argument represents end of range, not length
  4136.          * @returns {Range}
  4137.          */
  4138.         create2: function(start, end) {
  4139.             if (_.isNumber(start) && _.isNumber(end)) {
  4140.                 end -= start;
  4141.             }
  4142.             
  4143.             return this.create(start, end);
  4144.         }
  4145.     };
  4146. });/**
  4147.  * Utility module that provides ordered storage of function handlers. 
  4148.  * Many Emmet modules' functionality can be extended/overridden by custom
  4149.  * function. This modules provides unified storage of handler functions, their 
  4150.  * management and execution
  4151.  * 
  4152.  * @constructor
  4153.  * @memberOf __handlerListDefine
  4154.  * @param {Function} require
  4155.  * @param {Underscore} _
  4156.  */
  4157. emmet.define('handlerList', function(require, _) {
  4158.     /**
  4159.      * @type HandlerList
  4160.      * @constructor
  4161.      */
  4162.     function HandlerList() {
  4163.         this._list = [];
  4164.     }
  4165.     
  4166.     HandlerList.prototype = {
  4167.         /**
  4168.          * Adds function handler
  4169.          * @param {Function} fn Handler
  4170.          * @param {Object} options Handler options. Possible values are:<br><br>
  4171.          * <b>order</b> : (<code>Number</code>) ΓÇô order in handler list. Handlers
  4172.          * with higher order value will be executed earlier.
  4173.          */
  4174.         add: function(fn, options) {
  4175.             this._list.push(_.extend({order: 0}, options || {}, {fn: fn}));
  4176.         },
  4177.         
  4178.         /**
  4179.          * Removes handler from list
  4180.          * @param {Function} fn
  4181.          */
  4182.         remove: function(fn) {
  4183.             this._list = _.without(this._list, _.find(this._list, function(item) {
  4184.                 return item.fn === fn;
  4185.             }));
  4186.         },
  4187.         
  4188.         /**
  4189.          * Returns ordered list of handlers. By default, handlers 
  4190.          * with the same <code>order</code> option returned in reverse order, 
  4191.          * i.e. the latter function was added into the handlers list, the higher 
  4192.          * it will be in the returned array 
  4193.          * @returns {Array}
  4194.          */
  4195.         list: function() {
  4196.             return _.sortBy(this._list, 'order').reverse();
  4197.         },
  4198.         
  4199.         /**
  4200.          * Returns ordered list of handler functions
  4201.          * @returns {Array}
  4202.          */
  4203.         listFn: function() {
  4204.             return _.pluck(this.list(), 'fn');
  4205.         },
  4206.         
  4207.         /**
  4208.          * Executes handler functions in their designated order. If function
  4209.          * returns <code>skipVal</code>, meaning that function was unable to 
  4210.          * handle passed <code>args</code>, the next function will be executed
  4211.          * and so on.
  4212.          * @param {Object} skipValue If function returns this value, execute 
  4213.          * next handler.
  4214.          * @param {Array} args Arguments to pass to handler function
  4215.          * @returns {Boolean} Whether any of registered handlers performed
  4216.          * successfully  
  4217.          */
  4218.         exec: function(skipValue, args) {
  4219.             args = args || [];
  4220.             var result = null;
  4221.             _.find(this.list(), function(h) {
  4222.                 result = h.fn.apply(h, args);
  4223.                 if (result !== skipValue)
  4224.                     return true;
  4225.             });
  4226.             
  4227.             return result;
  4228.         }
  4229.     };
  4230.     
  4231.     return {
  4232.         /**
  4233.          * Factory method that produces <code>HandlerList</code> instance
  4234.          * @returns {HandlerList}
  4235.          * @memberOf handlerList
  4236.          */
  4237.         create: function() {
  4238.             return new HandlerList();
  4239.         }
  4240.     };
  4241. });/**
  4242.  * Helper class for convenient token iteration
  4243.  */
  4244. emmet.define('tokenIterator', function(require, _) {
  4245.     /**
  4246.      * @type TokenIterator
  4247.      * @param {Array} tokens
  4248.      * @type TokenIterator
  4249.      * @constructor
  4250.      */
  4251.     function TokenIterator(tokens) {
  4252.         /** @type Array */
  4253.         this.tokens = tokens;
  4254.         this._position = 0;
  4255.         this.reset();
  4256.     }
  4257.     
  4258.     TokenIterator.prototype = {
  4259.         next: function() {
  4260.             if (this.hasNext()) {
  4261.                 var token = this.tokens[++this._i];
  4262.                 this._position = token.start;
  4263.                 return token;
  4264.             }
  4265.             
  4266.             return null;
  4267.         },
  4268.         
  4269.         current: function() {
  4270.             return this.tokens[this._i];
  4271.         },
  4272.         
  4273.         position: function() {
  4274.             return this._position;
  4275.         },
  4276.         
  4277.         hasNext: function() {
  4278.             return this._i < this._il - 1;
  4279.         },
  4280.         
  4281.         reset: function() {
  4282.             this._i = -1;
  4283.             this._il = this.tokens.length;
  4284.         },
  4285.         
  4286.         item: function() {
  4287.             return this.tokens[this._i];
  4288.         },
  4289.         
  4290.         itemNext: function() {
  4291.             return this.tokens[this._i + 1];
  4292.         },
  4293.         
  4294.         itemPrev: function() {
  4295.             return this.tokens[this._i - 1];
  4296.         },
  4297.         
  4298.         nextUntil: function(type, callback) {
  4299.             var token;
  4300.             var test = _.isString(type) 
  4301.                 ? function(t){return t.type == type;} 
  4302.                 : type;
  4303.             
  4304.             while (token = this.next()) {
  4305.                 if (callback)
  4306.                     callback.call(this, token);
  4307.                 if (test.call(this, token))
  4308.                     break;
  4309.             }
  4310.         }
  4311.     };
  4312.     
  4313.     return {
  4314.         create: function(tokens) {
  4315.             return new TokenIterator(tokens);
  4316.         }
  4317.     };
  4318. });/**
  4319.  * A trimmed version of CodeMirror's StringStream module for string parsing
  4320.  */
  4321. emmet.define('stringStream', function(require, _) {
  4322.     /**
  4323.      * @type StringStream
  4324.      * @constructor
  4325.      * @param {String} string
  4326.      */
  4327.     function StringStream(string) {
  4328.         this.pos = this.start = 0;
  4329.         this.string = string;
  4330.     }
  4331.     
  4332.     StringStream.prototype = {
  4333.         /**
  4334.          * Returns true only if the stream is at the end of the line.
  4335.          * @returns {Boolean}
  4336.          */
  4337.         eol: function() {
  4338.             return this.pos >= this.string.length;
  4339.         },
  4340.         
  4341.         /**
  4342.          * Returns true only if the stream is at the start of the line
  4343.          * @returns {Boolean}
  4344.          */
  4345.         sol: function() {
  4346.             return this.pos == 0;
  4347.         },
  4348.         
  4349.         /**
  4350.          * Returns the next character in the stream without advancing it. 
  4351.          * Will return <code>undefined</code> at the end of the line.
  4352.          * @returns {String}
  4353.          */
  4354.         peek: function() {
  4355.             return this.string.charAt(this.pos);
  4356.         },
  4357.         
  4358.         /**
  4359.          * Returns the next character in the stream and advances it.
  4360.          * Also returns <code>undefined</code> when no more characters are available.
  4361.          * @returns {String}
  4362.          */
  4363.         next: function() {
  4364.             if (this.pos < this.string.length)
  4365.                 return this.string.charAt(this.pos++);
  4366.         },
  4367.         
  4368.         /**
  4369.          * match can be a character, a regular expression, or a function that
  4370.          * takes a character and returns a boolean. If the next character in the
  4371.          * stream 'matches' the given argument, it is consumed and returned.
  4372.          * Otherwise, undefined is returned.
  4373.          * @param {Object} match
  4374.          * @returns {String}
  4375.          */
  4376.         eat: function(match) {
  4377.             var ch = this.string.charAt(this.pos), ok;
  4378.             if (typeof match == "string")
  4379.                 ok = ch == match;
  4380.             else
  4381.                 ok = ch && (match.test ? match.test(ch) : match(ch));
  4382.             
  4383.             if (ok) {
  4384.                 ++this.pos;
  4385.                 return ch;
  4386.             }
  4387.         },
  4388.         
  4389.         /**
  4390.          * Repeatedly calls <code>eat</code> with the given argument, until it
  4391.          * fails. Returns <code>true</code> if any characters were eaten.
  4392.          * @param {Object} match
  4393.          * @returns {Boolean}
  4394.          */
  4395.         eatWhile: function(match) {
  4396.             var start = this.pos;
  4397.             while (this.eat(match)) {}
  4398.             return this.pos > start;
  4399.         },
  4400.         
  4401.         /**
  4402.          * Shortcut for <code>eatWhile</code> when matching white-space.
  4403.          * @returns {Boolean}
  4404.          */
  4405.         eatSpace: function() {
  4406.             var start = this.pos;
  4407.             while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
  4408.                 ++this.pos;
  4409.             return this.pos > start;
  4410.         },
  4411.         
  4412.         /**
  4413.          * Moves the position to the end of the line.
  4414.          */
  4415.         skipToEnd: function() {
  4416.             this.pos = this.string.length;
  4417.         },
  4418.         
  4419.         /**
  4420.          * Skips to the next occurrence of the given character, if found on the
  4421.          * current line (doesn't advance the stream if the character does not
  4422.          * occur on the line). Returns true if the character was found.
  4423.          * @param {String} ch
  4424.          * @returns {Boolean}
  4425.          */
  4426.         skipTo: function(ch) {
  4427.             var found = this.string.indexOf(ch, this.pos);
  4428.             if (found > -1) {
  4429.                 this.pos = found;
  4430.                 return true;
  4431.             }
  4432.         },
  4433.         
  4434.         /**
  4435.          * Skips to <code>close</code> character which is pair to <code>open</code>
  4436.          * character, considering possible pair nesting. This function is used
  4437.          * to consume pair of characters, like opening and closing braces
  4438.          * @param {String} open
  4439.          * @param {String} close
  4440.          * @returns {Boolean} Returns <code>true</code> if pair was successfully
  4441.          * consumed
  4442.          */
  4443.         skipToPair: function(open, close) {
  4444.             var braceCount = 0, ch;
  4445.             var pos = this.pos, len = this.string.length;
  4446.             while (pos < len) {
  4447.                 ch = this.string.charAt(pos++);
  4448.                 if (ch == open) {
  4449.                     braceCount++;
  4450.                 } else if (ch == close) {
  4451.                     braceCount--;
  4452.                     if (braceCount < 1) {
  4453.                         this.pos = pos;
  4454.                         return true;
  4455.                     }
  4456.                 }
  4457.             }
  4458.             
  4459.             return false;
  4460.         },
  4461.         
  4462.         /**
  4463.          * Backs up the stream n characters. Backing it up further than the
  4464.          * start of the current token will cause things to break, so be careful.
  4465.          * @param {Number} n
  4466.          */
  4467.         backUp : function(n) {
  4468.             this.pos -= n;
  4469.         },
  4470.         
  4471.         /**
  4472.          * Act like a multi-character <code>eat</code>ΓÇöif <code>consume</code> is true or
  4473.          * not givenΓÇöor a look-ahead that doesn't update the stream positionΓÇöif
  4474.          * it is false. <code>pattern</code> can be either a string or a
  4475.          * regular expression starting with ^. When it is a string,
  4476.          * <code>caseInsensitive</code> can be set to true to make the match
  4477.          * case-insensitive. When successfully matching a regular expression,
  4478.          * the returned value will be the array returned by <code>match</code>,
  4479.          * in case you need to extract matched groups.
  4480.          * 
  4481.          * @param {RegExp} pattern
  4482.          * @param {Boolean} consume
  4483.          * @param {Boolean} caseInsensitive
  4484.          * @returns
  4485.          */
  4486.         match: function(pattern, consume, caseInsensitive) {
  4487.             if (typeof pattern == "string") {
  4488.                 var cased = caseInsensitive
  4489.                     ? function(str) {return str.toLowerCase();}
  4490.                     : function(str) {return str;};
  4491.                 
  4492.                 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
  4493.                     if (consume !== false)
  4494.                         this.pos += pattern.length;
  4495.                     return true;
  4496.                 }
  4497.             } else {
  4498.                 var match = this.string.slice(this.pos).match(pattern);
  4499.                 if (match && consume !== false)
  4500.                     this.pos += match[0].length;
  4501.                 return match;
  4502.             }
  4503.         },
  4504.         
  4505.         /**
  4506.          * Get the string between the start of the current token and the 
  4507.          * current stream position.
  4508.          * @returns {String}
  4509.          */
  4510.         current: function() {
  4511.             return this.string.slice(this.start, this.pos);
  4512.         }
  4513.     };
  4514.     
  4515.     return {
  4516.         create: function(string) {
  4517.             return new StringStream(string);
  4518.         }
  4519.     };
  4520. });/**
  4521.  * Parsed resources (snippets, abbreviations, variables, etc.) for Emmet.
  4522.  * Contains convenient method to get access for snippets with respect of 
  4523.  * inheritance. Also provides ability to store data in different vocabularies
  4524.  * ('system' and 'user') for fast and safe resource update
  4525.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  4526.  * @link http://chikuyonok.ru
  4527.  * 
  4528.  * @param {Function} require
  4529.  * @param {Underscore} _
  4530.  */
  4531. emmet.define('resources', function(require, _) {
  4532.     var VOC_SYSTEM = 'system';
  4533.     var VOC_USER = 'user';
  4534.     
  4535.     var cache = {};
  4536.         
  4537.     /** Regular expression for XML tag matching */
  4538.     var reTag = /^<(\w+\:?[\w\-]*)((?:\s+[\w\:\-]+\s*=\s*(['"]).*?\3)*)\s*(\/?)>/;
  4539.         
  4540.     var systemSettings = {};
  4541.     var userSettings = {};
  4542.     
  4543.     /** @type HandlerList List of registered abbreviation resolvers */
  4544.     var resolvers = require('handlerList').create();
  4545.     
  4546.     /**
  4547.      * Normalizes caret plceholder in passed text: replaces | character with
  4548.      * default caret placeholder
  4549.      * @param {String} text
  4550.      * @returns {String}
  4551.      */
  4552.     function normalizeCaretPlaceholder(text) {
  4553.         var utils = require('utils');
  4554.         return utils.replaceUnescapedSymbol(text, '|', utils.getCaretPlaceholder());
  4555.     }
  4556.     
  4557.     function parseItem(name, value, type) {
  4558.         value = normalizeCaretPlaceholder(value);
  4559.         
  4560.         if (type == 'snippets') {
  4561.             return require('elements').create('snippet', value);
  4562.         }
  4563.         
  4564.         if (type == 'abbreviations') {
  4565.             return parseAbbreviation(name, value);
  4566.         }
  4567.     }
  4568.     
  4569.     /**
  4570.      * Parses single abbreviation
  4571.      * @param {String} key Abbreviation name
  4572.      * @param {String} value Abbreviation value
  4573.      * @return {Object}
  4574.      */
  4575.     function parseAbbreviation(key, value) {
  4576.         key = require('utils').trim(key);
  4577.         var elements = require('elements');
  4578.         var m;
  4579.         if (m = reTag.exec(value)) {
  4580.             return elements.create('element', m[1], m[2], m[4] == '/');
  4581.         } else {
  4582.             // assume it's reference to another abbreviation
  4583.             return elements.create('reference', value);
  4584.         }
  4585.     }
  4586.     
  4587.     /**
  4588.      * Normalizes snippet key name for better fuzzy search
  4589.      * @param {String} str
  4590.      * @returns {String}
  4591.      */
  4592.     function normalizeName(str) {
  4593.         return str.replace(/:$/, '').replace(/:/g, '-');
  4594.     }
  4595.     
  4596.     return {
  4597.         /**
  4598.          * Sets new unparsed data for specified settings vocabulary
  4599.          * @param {Object} data
  4600.          * @param {String} type Vocabulary type ('system' or 'user')
  4601.          * @memberOf resources
  4602.          */
  4603.         setVocabulary: function(data, type) {
  4604.             cache = {};
  4605.             if (type == VOC_SYSTEM)
  4606.                 systemSettings = data;
  4607.             else
  4608.                 userSettings = data;
  4609.         },
  4610.         
  4611.         /**
  4612.          * Returns resource vocabulary by its name
  4613.          * @param {String} name Vocabulary name ('system' or 'user')
  4614.          * @return {Object}
  4615.          */
  4616.         getVocabulary: function(name) {
  4617.             return name == VOC_SYSTEM ? systemSettings : userSettings;
  4618.         },
  4619.         
  4620.         /**
  4621.          * Returns resource (abbreviation, snippet, etc.) matched for passed 
  4622.          * abbreviation
  4623.          * @param {TreeNode} node
  4624.          * @param {String} syntax
  4625.          * @returns {Object}
  4626.          */
  4627.         getMatchedResource: function(node, syntax) {
  4628.             return resolvers.exec(null, _.toArray(arguments)) 
  4629.                 || this.findSnippet(syntax, node.name());
  4630.         },
  4631.         
  4632.         /**
  4633.          * Returns variable value
  4634.          * @return {String}
  4635.          */
  4636.         getVariable: function(name) {
  4637.             return (this.getSection('variables') || {})[name];
  4638.         },
  4639.         
  4640.         /**
  4641.          * Store runtime variable in user storage
  4642.          * @param {String} name Variable name
  4643.          * @param {String} value Variable value
  4644.          */
  4645.         setVariable: function(name, value){
  4646.             var voc = this.getVocabulary('user') || {};
  4647.             if (!('variables' in voc))
  4648.                 voc.variables = {};
  4649.                 
  4650.             voc.variables[name] = value;
  4651.             this.setVocabulary(voc, 'user');
  4652.         },
  4653.         
  4654.         /**
  4655.          * Check if there are resources for specified syntax
  4656.          * @param {String} syntax
  4657.          * @return {Boolean}
  4658.          */
  4659.         hasSyntax: function(syntax) {
  4660.             return syntax in this.getVocabulary(VOC_USER) 
  4661.                 || syntax in this.getVocabulary(VOC_SYSTEM);
  4662.         },
  4663.         
  4664.         /**
  4665.          * Registers new abbreviation resolver.
  4666.          * @param {Function} fn Abbreviation resolver which will receive 
  4667.          * abbreviation as first argument and should return parsed abbreviation
  4668.          * object if abbreviation has handled successfully, <code>null</code>
  4669.          * otherwise
  4670.          * @param {Object} options Options list as described in 
  4671.          * {@link HandlerList#add()} method
  4672.          */
  4673.         addResolver: function(fn, options) {
  4674.             resolvers.add(fn, options);
  4675.         },
  4676.         
  4677.         removeResolver: function(fn) {
  4678.             resolvers.remove(fn);
  4679.         },
  4680.         
  4681.         /**
  4682.          * Returns actual section data, merged from both
  4683.          * system and user data
  4684.          * @param {String} name Section name (syntax)
  4685.          * @param {String} ...args Subsections
  4686.          * @returns
  4687.          */
  4688.         getSection: function(name) {
  4689.             if (!name)
  4690.                 return null;
  4691.             
  4692.             if (!(name in cache)) {
  4693.                 cache[name] = require('utils').deepMerge({}, systemSettings[name], userSettings[name]);
  4694.             }
  4695.             
  4696.             var data = cache[name], subsections = _.rest(arguments), key;
  4697.             while (data && (key = subsections.shift())) {
  4698.                 if (key in data) {
  4699.                     data = data[key];
  4700.                 } else {
  4701.                     return null;
  4702.                 }
  4703.             }
  4704.             
  4705.             return data;
  4706.         },
  4707.         
  4708.         /**
  4709.          * Recursively searches for a item inside top level sections (syntaxes)
  4710.          * with respect of `extends` attribute
  4711.          * @param {String} topSection Top section name (syntax)
  4712.          * @param {String} subsection Inner section name
  4713.          * @returns {Object}
  4714.          */
  4715.         findItem: function(topSection, subsection) {
  4716.             var data = this.getSection(topSection);
  4717.             while (data) {
  4718.                 if (subsection in data)
  4719.                     return data[subsection];
  4720.                 
  4721.                 data = this.getSection(data['extends']);
  4722.             }
  4723.         },
  4724.         
  4725.         /**
  4726.          * Recursively searches for a snippet definition inside syntax section.
  4727.          * Definition is searched inside `snippets` and `abbreviations` 
  4728.          * subsections  
  4729.          * @param {String} syntax Top-level section name (syntax)
  4730.          * @param {String} name Snippet name
  4731.          * @returns {Object}
  4732.          */
  4733.         findSnippet: function(syntax, name, memo) {
  4734.             if (!syntax || !name)
  4735.                 return null;
  4736.             
  4737.             memo = memo || [];
  4738.             
  4739.             var names = [name];
  4740.             // create automatic aliases to properties with colons,
  4741.             // e.g. pos-a == pos:a
  4742.             if (~name.indexOf('-'))
  4743.                 names.push(name.replace(/\-/g, ':'));
  4744.             
  4745.             var data = this.getSection(syntax), matchedItem = null;
  4746.             _.find(['snippets', 'abbreviations'], function(sectionName) {
  4747.                 var data = this.getSection(syntax, sectionName);
  4748.                 if (data) {
  4749.                     return _.find(names, function(n) {
  4750.                         if (data[n])
  4751.                             return matchedItem = parseItem(n, data[n], sectionName);
  4752.                     });
  4753.                 }
  4754.             }, this);
  4755.             
  4756.             memo.push(syntax);
  4757.             if (!matchedItem && data['extends'] && !_.include(memo, data['extends'])) {
  4758.                 // try to find item in parent syntax section
  4759.                 return this.findSnippet(data['extends'], name, memo);
  4760.             }
  4761.             
  4762.             return matchedItem;
  4763.         },
  4764.         
  4765.         /**
  4766.          * Performs fuzzy search of snippet definition
  4767.          * @param {String} syntax Top-level section name (syntax)
  4768.          * @param {String} name Snippet name
  4769.          * @returns
  4770.          */
  4771.         fuzzyFindSnippet: function(syntax, name, minScore) {
  4772.             minScore = minScore || 0.3;
  4773.             
  4774.             var payload = this.getAllSnippets(syntax);
  4775.             var sc = require('string-score');
  4776.             
  4777.             name = normalizeName(name);
  4778.             var scores = _.map(payload, function(value, key) {
  4779.                 return {
  4780.                     key: key,
  4781.                     score: sc.score(value.nk, name, 0.1)
  4782.                 };
  4783.             });
  4784.             
  4785.             var result = _.last(_.sortBy(scores, 'score'));
  4786.             if (result && result.score >= minScore) {
  4787.                 var k = result.key;
  4788.                 return payload[k].parsedValue;
  4789. //                return parseItem(k, payload[k].value, payload[k].type);
  4790.             }
  4791.         },
  4792.         
  4793.         /**
  4794.          * Returns plain dictionary of all available abbreviations and snippets
  4795.          * for specified syntax with respect of inheritance
  4796.          * @param {String} syntax
  4797.          * @returns {Object}
  4798.          */
  4799.         getAllSnippets: function(syntax) {
  4800.             var cacheKey = 'all-' + syntax;
  4801.             if (!cache[cacheKey]) {
  4802.                 var stack = [], sectionKey = syntax;
  4803.                 var memo = [];
  4804.                 
  4805.                 do {
  4806.                     var section = this.getSection(sectionKey);
  4807.                     if (!section)
  4808.                         break;
  4809.                     
  4810.                     _.each(['snippets', 'abbreviations'], function(sectionName) {
  4811.                         var stackItem = {};
  4812.                         _.each(section[sectionName] || null, function(v, k) {
  4813.                             stackItem[k] = {
  4814.                                 nk: normalizeName(k),
  4815.                                 value: v,
  4816.                                 parsedValue: parseItem(k, v, sectionName),
  4817.                                 type: sectionName
  4818.                             };
  4819.                         });
  4820.                         
  4821.                         stack.push(stackItem);
  4822.                     });
  4823.                     
  4824.                     memo.push(sectionKey);
  4825.                     sectionKey = section['extends'];
  4826.                 } while (sectionKey && !_.include(memo, sectionKey));
  4827.                 
  4828.                 
  4829.                 cache[cacheKey] = _.extend.apply(_, stack.reverse());
  4830.             }
  4831.             
  4832.             return cache[cacheKey];
  4833.         }
  4834.     };
  4835. });/**
  4836.  * Module describes and performs Emmet actions. The actions themselves are
  4837.  * defined in <i>actions</i> folder
  4838.  * @param {Function} require
  4839.  * @param {Underscore} _
  4840.  */
  4841. emmet.define('actions', function(require, _, zc) {
  4842.     var actions = {};
  4843.     
  4844.     /**
  4845.      * ΓÇ£HumanizesΓÇ¥ action name, makes it more readable for people
  4846.      * @param {String} name Action name (like 'expand_abbreviation')
  4847.      * @return Humanized name (like 'Expand Abbreviation')
  4848.      */
  4849.     function humanizeActionName(name) {
  4850.         return require('utils').trim(name.charAt(0).toUpperCase() 
  4851.             + name.substring(1).replace(/_[a-z]/g, function(str) {
  4852.                 return ' ' + str.charAt(1).toUpperCase();
  4853.             }));
  4854.     }
  4855.     
  4856.     return {
  4857.         /**
  4858.          * Registers new action
  4859.          * @param {String} name Action name
  4860.          * @param {Function} fn Action function
  4861.          * @param {Object} options Custom action options:<br>
  4862.          * <b>label</b> : (<code>String</code>) ΓÇô Human-readable action name. 
  4863.          * May contain '/' symbols as submenu separators<br>
  4864.          * <b>hidden</b> : (<code>Boolean</code>) ΓÇô Indicates whether action
  4865.          * should be displayed in menu (<code>getMenu()</code> method)
  4866.          * 
  4867.          * @memberOf actions
  4868.          */
  4869.         add: function(name, fn, options) {
  4870.             name = name.toLowerCase();
  4871.             options = options || {};
  4872.             if (!options.label) {
  4873.                 options.label = humanizeActionName(name);
  4874.             }
  4875.             
  4876.             actions[name] = {
  4877.                 name: name,
  4878.                 fn: fn,
  4879.                 options: options
  4880.             };
  4881.         },
  4882.         
  4883.         /**
  4884.          * Returns action object
  4885.          * @param {String} name Action name
  4886.          * @returns {Object}
  4887.          */
  4888.         get: function(name) {
  4889.             return actions[name.toLowerCase()];
  4890.         },
  4891.         
  4892.         /**
  4893.          * Runs Emmet action. For list of available actions and their
  4894.          * arguments see <i>actions</i> folder.
  4895.          * @param {String} name Action name 
  4896.          * @param {Array} args Additional arguments. It may be array of arguments
  4897.          * or inline arguments. The first argument should be <code>IEmmetEditor</code> instance
  4898.          * @returns {Boolean} Status of performed operation, <code>true</code>
  4899.          * means action was performed successfully.
  4900.          * @example
  4901.          * emmet.require('actions').run('expand_abbreviation', editor);  
  4902.          * emmet.require('actions').run('wrap_with_abbreviation', [editor, 'div']);  
  4903.          */
  4904.         run: function(name, args) {
  4905.             if (!_.isArray(args)) {
  4906.                 args = _.rest(arguments);
  4907.             }
  4908.             
  4909.             var action = this.get(name);
  4910.             if (action) {
  4911.                 return action.fn.apply(emmet, args);
  4912.             } else {
  4913.                 emmet.log('Action "%s" is not defined', name);
  4914.                 return false;
  4915.             }
  4916.         },
  4917.         
  4918.         /**
  4919.          * Returns all registered actions as object
  4920.          * @returns {Object}
  4921.          */
  4922.         getAll: function() {
  4923.             return actions;
  4924.         },
  4925.         
  4926.         /**
  4927.          * Returns all registered actions as array
  4928.          * @returns {Array}
  4929.          */
  4930.         getList: function() {
  4931.             return _.values(this.getAll());
  4932.         },
  4933.         
  4934.         /**
  4935.          * Returns actions list as structured menu. If action has <i>label</i>,
  4936.          * it will be splitted by '/' symbol into submenus (for example: 
  4937.          * CSS/Reflect Value) and grouped with other items
  4938.          * @param {Array} skipActions List of action identifiers that should be 
  4939.          * skipped from menu
  4940.          * @returns {Array}
  4941.          */
  4942.         getMenu: function(skipActions) {
  4943.             var result = [];
  4944.             skipActions = skipActions || [];
  4945.             _.each(this.getList(), function(action) {
  4946.                 if (action.options.hidden || _.include(skipActions, action.name))
  4947.                     return;
  4948.                 
  4949.                 var actionName = humanizeActionName(action.name);
  4950.                 var ctx = result;
  4951.                 if (action.options.label) {
  4952.                     var parts = action.options.label.split('/');
  4953.                     actionName = parts.pop();
  4954.                     
  4955.                     // create submenus, if needed
  4956.                     var menuName, submenu;
  4957.                     while (menuName = parts.shift()) {
  4958.                         submenu = _.find(ctx, function(item) {
  4959.                             return item.type == 'submenu' && item.name == menuName;
  4960.                         });
  4961.                         
  4962.                         if (!submenu) {
  4963.                             submenu = {
  4964.                                 name: menuName,
  4965.                                 type: 'submenu',
  4966.                                 items: []
  4967.                             };
  4968.                             ctx.push(submenu);
  4969.                         }
  4970.                         
  4971.                         ctx = submenu.items;
  4972.                     }
  4973.                 }
  4974.                 
  4975.                 ctx.push({
  4976.                     type: 'action',
  4977.                     name: action.name,
  4978.                     label: actionName
  4979.                 });
  4980.             });
  4981.             
  4982.             return result;
  4983.         },
  4984.  
  4985.         /**
  4986.          * Returns action name associated with menu item title
  4987.          * @param {String} title
  4988.          * @returns {String}
  4989.          */
  4990.         getActionNameForMenuTitle: function(title, menu) {
  4991.             var item = null;
  4992.             _.find(menu || this.getMenu(), function(val) {
  4993.                 if (val.type == 'action') {
  4994.                     if (val.label == title || val.name == title) {
  4995.                         return item = val.name;
  4996.                     }
  4997.                 } else {
  4998.                     return item = this.getActionNameForMenuTitle(title, val.items);
  4999.                 }
  5000.             }, this);
  5001.             
  5002.             return item || null;
  5003.         }
  5004.     };
  5005. });/**
  5006.  * Output profile module.
  5007.  * Profile defines how XHTML output data should look like
  5008.  * @param {Function} require
  5009.  * @param {Underscore} _
  5010.  */
  5011. emmet.define('profile', function(require, _) {
  5012.     var profiles = {};
  5013.     
  5014.     var defaultProfile = {
  5015.         tag_case: 'asis',
  5016.         attr_case: 'asis',
  5017.         attr_quotes: 'double',
  5018.         
  5019.         // each tag on new line
  5020.         tag_nl: 'decide',
  5021.         
  5022.         // with tag_nl === true, defines if leaf node (e.g. node with no children)
  5023.         // should have formatted line breaks
  5024.         tag_nl_leaf: false,
  5025.         
  5026.         place_cursor: true,
  5027.         
  5028.         // indent tags
  5029.         indent: true,
  5030.         
  5031.         // how many inline elements should be to force line break 
  5032.         // (set to 0 to disable)
  5033.         inline_break: 3,
  5034.         
  5035.         // use self-closing style for writing empty elements, e.g. <br /> or <br>
  5036.         self_closing_tag: 'xhtml',
  5037.         
  5038.         // Profile-level output filters, re-defines syntax filters 
  5039.         filters: '',
  5040.         
  5041.         // Additional filters applied to abbreviation.
  5042.         // Unlike "filters", this preference doesn't override default filters
  5043.         // but add the instead every time given profile is chosen
  5044.         extraFilters: ''
  5045.     };
  5046.     
  5047.     /**
  5048.      * @constructor
  5049.      * @type OutputProfile
  5050.      * @param {Object} options
  5051.      */
  5052.     function OutputProfile(options) {
  5053.         _.extend(this, defaultProfile, options);
  5054.     }
  5055.     
  5056.     OutputProfile.prototype = {
  5057.         /**
  5058.          * Transforms tag name case depending on current profile settings
  5059.          * @param {String} name String to transform
  5060.          * @returns {String}
  5061.          */
  5062.         tagName: function(name) {
  5063.             return stringCase(name, this.tag_case);
  5064.         },
  5065.         
  5066.         /**
  5067.          * Transforms attribute name case depending on current profile settings 
  5068.          * @param {String} name String to transform
  5069.          * @returns {String}
  5070.          */
  5071.         attributeName: function(name) {
  5072.             return stringCase(name, this.attr_case);
  5073.         },
  5074.         
  5075.         /**
  5076.          * Returns quote character for current profile
  5077.          * @returns {String}
  5078.          */
  5079.         attributeQuote: function() {
  5080.             return this.attr_quotes == 'single' ? "'" : '"';
  5081.         },
  5082.         
  5083.         /**
  5084.          * Returns self-closing tag symbol for current profile
  5085.          * @param {String} param
  5086.          * @returns {String}
  5087.          */
  5088.         selfClosing: function(param) {
  5089.             if (this.self_closing_tag == 'xhtml')
  5090.                 return ' /';
  5091.             
  5092.             if (this.self_closing_tag === true)
  5093.                 return '/';
  5094.             
  5095.             return '';
  5096.         },
  5097.         
  5098.         /**
  5099.          * Returns cursor token based on current profile settings
  5100.          * @returns {String}
  5101.          */
  5102.         cursor: function() {
  5103.             return this.place_cursor ? require('utils').getCaretPlaceholder() : '';
  5104.         }
  5105.     };
  5106.     
  5107.     /**
  5108.      * Helper function that converts string case depending on 
  5109.      * <code>caseValue</code> 
  5110.      * @param {String} str String to transform
  5111.      * @param {String} caseValue Case value: can be <i>lower</i>, 
  5112.      * <i>upper</i> and <i>leave</i>
  5113.      * @returns {String}
  5114.      */
  5115.     function stringCase(str, caseValue) {
  5116.         switch (String(caseValue || '').toLowerCase()) {
  5117.             case 'lower':
  5118.                 return str.toLowerCase();
  5119.             case 'upper':
  5120.                 return str.toUpperCase();
  5121.         }
  5122.         
  5123.         return str;
  5124.     }
  5125.     
  5126.     /**
  5127.      * Creates new output profile
  5128.      * @param {String} name Profile name
  5129.      * @param {Object} options Profile options
  5130.      */
  5131.     function createProfile(name, options) {
  5132.         return profiles[name.toLowerCase()] = new OutputProfile(options);
  5133.     }
  5134.     
  5135.     function createDefaultProfiles() {
  5136.         createProfile('xhtml');
  5137.         createProfile('html', {self_closing_tag: false});
  5138.         createProfile('xml', {self_closing_tag: true, tag_nl: true});
  5139.         createProfile('plain', {tag_nl: false, indent: false, place_cursor: false});
  5140.         createProfile('line', {tag_nl: false, indent: false, extraFilters: 's'});
  5141.     }
  5142.     
  5143.     createDefaultProfiles();
  5144.     
  5145.     return  {
  5146.         /**
  5147.          * Creates new output profile and adds it into internal dictionary
  5148.          * @param {String} name Profile name
  5149.          * @param {Object} options Profile options
  5150.          * @memberOf emmet.profile
  5151.          * @returns {Object} New profile
  5152.          */
  5153.         create: function(name, options) {
  5154.             if (arguments.length == 2)
  5155.                 return createProfile(name, options);
  5156.             else
  5157.                 // create profile object only
  5158.                 return new OutputProfile(_.defaults(name || {}, defaultProfile));
  5159.         },
  5160.         
  5161.         /**
  5162.          * Returns profile by its name. If profile wasn't found, returns
  5163.          * 'plain' profile
  5164.          * @param {String} name Profile name. Might be profile itself
  5165.          * @param {String} syntax. Optional. Current editor syntax. If defined,
  5166.          * profile is searched in resources first, then in predefined profiles
  5167.          * @returns {Object}
  5168.          */
  5169.         get: function(name, syntax) {
  5170.             if (!name && syntax) {
  5171.                 // search in user resources first
  5172.                 var profile = require('resources').findItem(syntax, 'profile');
  5173.                 if (profile) {
  5174.                     name = profile;
  5175.                 }
  5176.             }
  5177.             
  5178.             if (!name) {
  5179.                 return profiles.plain;
  5180.             }
  5181.             
  5182.             if (name instanceof OutputProfile) {
  5183.                 return name;
  5184.             }
  5185.             
  5186.             if (_.isString(name) && name.toLowerCase() in profiles) {
  5187.                 return profiles[name.toLowerCase()];
  5188.             }
  5189.             
  5190.             return this.create(name);
  5191.         },
  5192.         
  5193.         /**
  5194.          * Deletes profile with specified name
  5195.          * @param {String} name Profile name
  5196.          */
  5197.         remove: function(name) {
  5198.             name = (name || '').toLowerCase();
  5199.             if (name in profiles)
  5200.                 delete profiles[name];
  5201.         },
  5202.         
  5203.         /**
  5204.          * Resets all user-defined profiles
  5205.          */
  5206.         reset: function() {
  5207.             profiles = {};
  5208.             createDefaultProfiles();
  5209.         },
  5210.         
  5211.         /**
  5212.          * Helper function that converts string case depending on 
  5213.          * <code>caseValue</code> 
  5214.          * @param {String} str String to transform
  5215.          * @param {String} caseValue Case value: can be <i>lower</i>, 
  5216.          * <i>upper</i> and <i>leave</i>
  5217.          * @returns {String}
  5218.          */
  5219.         stringCase: stringCase
  5220.     };
  5221. });/**
  5222.  * Utility module used to prepare text for pasting into back-end editor
  5223.  * @param {Function} require
  5224.  * @param {Underscore} _
  5225.  * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
  5226.  */
  5227. emmet.define('editorUtils', function(require, _) {
  5228.     return  {
  5229.         /**
  5230.          * Check if cursor is placed inside XHTML tag
  5231.          * @param {String} html Contents of the document
  5232.          * @param {Number} caretPos Current caret position inside tag
  5233.          * @return {Boolean}
  5234.          */
  5235.         isInsideTag: function(html, caretPos) {
  5236.             var reTag = /^<\/?\w[\w\:\-]*.*?>/;
  5237.             
  5238.             // search left to find opening brace
  5239.             var pos = caretPos;
  5240.             while (pos > -1) {
  5241.                 if (html.charAt(pos) == '<') 
  5242.                     break;
  5243.                 pos--;
  5244.             }
  5245.             
  5246.             if (pos != -1) {
  5247.                 var m = reTag.exec(html.substring(pos));
  5248.                 if (m && caretPos > pos && caretPos < pos + m[0].length)
  5249.                     return true;
  5250.             }
  5251.             
  5252.             return false;
  5253.         },
  5254.         
  5255.         /**
  5256.          * Sanitizes incoming editor data and provides default values for
  5257.          * output-specific info
  5258.          * @param {IEmmetEditor} editor
  5259.          * @param {String} syntax
  5260.          * @param {String} profile
  5261.          */
  5262.         outputInfo: function(editor, syntax, profile) {
  5263.             // most of this code makes sense for Java/Rhino environment
  5264.             // because string that comes from Java are not actually JS string
  5265.             // but Java String object so the have to be explicitly converted
  5266.             // to native string
  5267.             profile = profile || editor.getProfileName();
  5268.             return  {
  5269.                 /** @memberOf outputInfo */
  5270.                 syntax: String(syntax || editor.getSyntax()),
  5271.                 profile: profile ? String(profile) : null,
  5272.                 content: String(editor.getContent())
  5273.             };
  5274.         },
  5275.         
  5276.         /**
  5277.          * Unindent content, thus preparing text for tag wrapping
  5278.          * @param {IEmmetEditor} editor Editor instance
  5279.          * @param {String} text
  5280.          * @return {String}
  5281.          */
  5282.         unindent: function(editor, text) {
  5283.             return require('utils').unindentString(text, this.getCurrentLinePadding(editor));
  5284.         },
  5285.         
  5286.         /**
  5287.          * Returns padding of current editor's line
  5288.          * @param {IEmmetEditor} Editor instance
  5289.          * @return {String}
  5290.          */
  5291.         getCurrentLinePadding: function(editor) {
  5292.             return require('utils').getLinePadding(editor.getCurrentLine());
  5293.         }
  5294.     };
  5295. });
  5296. /**
  5297.  * Utility methods for Emmet actions
  5298.  * @param {Function} require
  5299.  * @param {Underscore} _
  5300.  * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
  5301.  */
  5302. emmet.define('actionUtils', function(require, _) {
  5303.     return {
  5304.         mimeTypes: {
  5305.             'gif' : 'image/gif',
  5306.             'png' : 'image/png',
  5307.             'jpg' : 'image/jpeg',
  5308.             'jpeg': 'image/jpeg',
  5309.             'svg' : 'image/svg+xml',
  5310.             'html': 'text/html',
  5311.             'htm' : 'text/html'
  5312.         },
  5313.         
  5314.         /**
  5315.          * Extracts abbreviations from text stream, starting from the end
  5316.          * @param {String} str
  5317.          * @return {String} Abbreviation or empty string
  5318.          * @memberOf emmet.actionUtils
  5319.          */
  5320.         extractAbbreviation: function(str) {
  5321.             var curOffset = str.length;
  5322.             var startIndex = -1;
  5323.             var groupCount = 0;
  5324.             var braceCount = 0;
  5325.             var textCount = 0;
  5326.             
  5327.             var utils = require('utils');
  5328.             var parser = require('abbreviationParser');
  5329.             
  5330.             while (true) {
  5331.                 curOffset--;
  5332.                 if (curOffset < 0) {
  5333.                     // moved to the beginning of the line
  5334.                     startIndex = 0;
  5335.                     break;
  5336.                 }
  5337.                 
  5338.                 var ch = str.charAt(curOffset);
  5339.                 
  5340.                 if (ch == ']') {
  5341.                     braceCount++;
  5342.                 } else if (ch == '[') {
  5343.                     if (!braceCount) { // unexpected brace
  5344.                         startIndex = curOffset + 1;
  5345.                         break;
  5346.                     }
  5347.                     braceCount--;
  5348.                 } else if (ch == '}') {
  5349.                     textCount++;
  5350.                 } else if (ch == '{') {
  5351.                     if (!textCount) { // unexpected brace
  5352.                         startIndex = curOffset + 1;
  5353.                         break;
  5354.                     }
  5355.                     textCount--;
  5356.                 } else if (ch == ')') {
  5357.                     groupCount++;
  5358.                 } else if (ch == '(') {
  5359.                     if (!groupCount) { // unexpected brace
  5360.                         startIndex = curOffset + 1;
  5361.                         break;
  5362.                     }
  5363.                     groupCount--;
  5364.                 } else {
  5365.                     if (braceCount || textCount) 
  5366.                         // respect all characters inside attribute sets or text nodes
  5367.                         continue;
  5368.                     else if (!parser.isAllowedChar(ch) || (ch == '>' && utils.endsWithTag(str.substring(0, curOffset + 1)))) {
  5369.                         // found stop symbol
  5370.                         startIndex = curOffset + 1;
  5371.                         break;
  5372.                     }
  5373.                 }
  5374.             }
  5375.             
  5376.             if (startIndex != -1 && !textCount && !braceCount && !groupCount) 
  5377.                 // found something, remove some invalid symbols from the 
  5378.                 // beginning and return abbreviation
  5379.                 return str.substring(startIndex).replace(/^[\*\+\>\^]+/, '');
  5380.             else
  5381.                 return '';
  5382.         },
  5383.         
  5384.         /**
  5385.          * Gets image size from image byte stream.
  5386.          * @author http://romeda.org/rePublish/
  5387.          * @param {String} stream Image byte stream (use <code>IEmmetFile.read()</code>)
  5388.          * @return {Object} Object with <code>width</code> and <code>height</code> properties
  5389.          */
  5390.         getImageSize: function(stream) {
  5391.             var pngMagicNum = "\211PNG\r\n\032\n",
  5392.                 jpgMagicNum = "\377\330",
  5393.                 gifMagicNum = "GIF8",
  5394.                 nextByte = function() {
  5395.                     return stream.charCodeAt(pos++);
  5396.                 };
  5397.         
  5398.             if (stream.substr(0, 8) === pngMagicNum) {
  5399.                 // PNG. Easy peasy.
  5400.                 var pos = stream.indexOf('IHDR') + 4;
  5401.             
  5402.                 return { width:  (nextByte() << 24) | (nextByte() << 16) |
  5403.                                  (nextByte() <<  8) | nextByte(),
  5404.                          height: (nextByte() << 24) | (nextByte() << 16) |
  5405.                                  (nextByte() <<  8) | nextByte() };
  5406.             
  5407.             } else if (stream.substr(0, 4) === gifMagicNum) {
  5408.                 pos = 6;
  5409.             
  5410.                 return {
  5411.                     width:  nextByte() | (nextByte() << 8),
  5412.                     height: nextByte() | (nextByte() << 8)
  5413.                 };
  5414.             
  5415.             } else if (stream.substr(0, 2) === jpgMagicNum) {
  5416.                 pos = 2;
  5417.             
  5418.                 var l = stream.length;
  5419.                 while (pos < l) {
  5420.                     if (nextByte() != 0xFF) return;
  5421.                 
  5422.                     var marker = nextByte();
  5423.                     if (marker == 0xDA) break;
  5424.                 
  5425.                     var size = (nextByte() << 8) | nextByte();
  5426.                 
  5427.                     if (marker >= 0xC0 && marker <= 0xCF && !(marker & 0x4) && !(marker & 0x8)) {
  5428.                         pos += 1;
  5429.                         return { height:  (nextByte() << 8) | nextByte(),
  5430.                                  width: (nextByte() << 8) | nextByte() };
  5431.                 
  5432.                     } else {
  5433.                         pos += size - 2;
  5434.                     }
  5435.                 }
  5436.             }
  5437.         },
  5438.         
  5439.         /**
  5440.          * Captures context XHTML element from editor under current caret position.
  5441.          * This node can be used as a helper for abbreviation extraction
  5442.          * @param {IEmmetEditor} editor
  5443.          * @returns {Object}
  5444.          */
  5445.         captureContext: function(editor) {
  5446.             var allowedSyntaxes = {'html': 1, 'xml': 1, 'xsl': 1};
  5447.             var syntax = String(editor.getSyntax());
  5448.             if (syntax in allowedSyntaxes) {
  5449.                 var content = String(editor.getContent());
  5450.                 var tag = require('htmlMatcher').find(content, editor.getCaretPos());
  5451.                 
  5452.                 if (tag && tag.type == 'tag') {
  5453.                     var reAttr = /([\w\-:]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
  5454.                     var startTag = tag.open;
  5455.                     var tagAttrs = startTag.range.substring(content).replace(/^<[\w\-\:]+/, '');
  5456. //                    var tagAttrs = startTag.full_tag.replace(/^<[\w\-\:]+/, '');
  5457.                     var contextNode = {
  5458.                         name: startTag.name,
  5459.                         attributes: []
  5460.                     };
  5461.                     
  5462.                     // parse attributes
  5463.                     var m;
  5464.                     while (m = reAttr.exec(tagAttrs)) {
  5465.                         contextNode.attributes.push({
  5466.                             name: m[1],
  5467.                             value: m[2]
  5468.                         });
  5469.                     }
  5470.                     
  5471.                     return contextNode;
  5472.                 }
  5473.             }
  5474.             
  5475.             return null;
  5476.         },
  5477.         
  5478.         /**
  5479.          * Find expression bounds in current editor at caret position. 
  5480.          * On each character a <code>fn</code> function will be called and must 
  5481.          * return <code>true</code> if current character meets requirements, 
  5482.          * <code>false</code> otherwise
  5483.          * @param {IEmmetEditor} editor
  5484.          * @param {Function} fn Function to test each character of expression
  5485.          * @return {Range}
  5486.          */
  5487.         findExpressionBounds: function(editor, fn) {
  5488.             var content = String(editor.getContent());
  5489.             var il = content.length;
  5490.             var exprStart = editor.getCaretPos() - 1;
  5491.             var exprEnd = exprStart + 1;
  5492.                 
  5493.             // start by searching left
  5494.             while (exprStart >= 0 && fn(content.charAt(exprStart), exprStart, content)) exprStart--;
  5495.             
  5496.             // then search right
  5497.             while (exprEnd < il && fn(content.charAt(exprEnd), exprEnd, content)) exprEnd++;
  5498.             
  5499.             if (exprEnd > exprStart) {
  5500.                 return require('range').create([++exprStart, exprEnd]);
  5501.             }
  5502.         },
  5503.         
  5504.         /**
  5505.          * @param {IEmmetEditor} editor
  5506.          * @param {Object} data
  5507.          * @returns {Boolean}
  5508.          */
  5509.         compoundUpdate: function(editor, data) {
  5510.             if (data) {
  5511.                 var sel = editor.getSelectionRange();
  5512.                 editor.replaceContent(data.data, data.start, data.end, true);
  5513.                 editor.createSelection(data.caret, data.caret + sel.end - sel.start);
  5514.                 return true;
  5515.             }
  5516.             
  5517.             return false;
  5518.         },
  5519.         
  5520.         /**
  5521.          * Common syntax detection method for editors that doesnΓÇÖt provide any
  5522.          * info about current syntax scope. 
  5523.          * @param {IEmmetEditor} editor Current editor
  5524.          * @param {String} hint Any syntax hint that editor can provide 
  5525.          * for syntax detection. Default is 'html'
  5526.          * @returns {String} 
  5527.          */
  5528.         detectSyntax: function(editor, hint) {
  5529.             var syntax = hint || 'html';
  5530.             
  5531.             if (!require('resources').hasSyntax(syntax)) {
  5532.                 syntax = 'html';
  5533.             }
  5534.             
  5535.             if (syntax == 'html' && (this.isStyle(editor) || this.isInlineCSS(editor))) {
  5536.                 syntax = 'css';
  5537.             }
  5538.             
  5539.             return syntax;
  5540.         },
  5541.         
  5542.         /**
  5543.          * Common method for detecting output profile
  5544.          * @param {IEmmetEditor} editor
  5545.          * @returns {String}
  5546.          */
  5547.         detectProfile: function(editor) {
  5548.             var syntax = editor.getSyntax();
  5549.             
  5550.             // get profile from syntax definition
  5551.             var profile = require('resources').findItem(syntax, 'profile');
  5552.             if (profile) {
  5553.                 return profile;
  5554.             }
  5555.             
  5556.             switch(syntax) {
  5557.                 case 'xml':
  5558.                 case 'xsl':
  5559.                     return 'xml';
  5560.                 case 'css':
  5561.                     if (this.isInlineCSS(editor)) {
  5562.                         return 'line';
  5563.                     }
  5564.                     break;
  5565.                 case 'html':
  5566.                     var profile = require('resources').getVariable('profile');
  5567.                     if (!profile) { // no forced profile, guess from content
  5568.                         // html or xhtml?
  5569.                         profile = this.isXHTML(editor) ? 'xhtml': 'html';
  5570.                     }
  5571.  
  5572.                     return profile;
  5573.             }
  5574.  
  5575.             return 'xhtml';
  5576.         },
  5577.         
  5578.         /**
  5579.          * Tries to detect if current document is XHTML one.
  5580.          * @param {IEmmetEditor} editor
  5581.          * @returns {Boolean}
  5582.          */
  5583.         isXHTML: function(editor) {
  5584.             return editor.getContent().search(/<!DOCTYPE[^>]+XHTML/i) != -1;
  5585.         },
  5586.         
  5587.         /**
  5588.          * Check if current caret position is inside <style> tag
  5589.          * @param {IEmmetEditor} editor
  5590.          * @returns
  5591.          */
  5592.         isStyle: function(editor) {
  5593.             var content = String(editor.getContent());
  5594.             var caretPos = editor.getCaretPos();
  5595.             var tag = require('htmlMatcher').tag(content, caretPos);
  5596.             return tag && tag.open.name.toLowerCase() == 'style' 
  5597.                 && tag.innerRange.cmp(caretPos, 'lte', 'gte');
  5598.         },
  5599.         
  5600.         /**
  5601.          * Check if current caret position is inside "style" attribute of HTML
  5602.          * element
  5603.          * @param {IEmmetEditor} editor
  5604.          * @returns {Boolean}
  5605.          */
  5606.         isInlineCSS: function(editor) {
  5607.             var content = String(editor.getContent());
  5608.             var caretPos = editor.getCaretPos();
  5609.             var tree = require('xmlEditTree').parseFromPosition(content, caretPos, true);
  5610.             if (tree) {
  5611.                 var attr = tree.itemFromPosition(caretPos, true);
  5612.                 return attr && attr.name().toLowerCase() == 'style' 
  5613.                     && attr.valueRange(true).cmp(caretPos, 'lte', 'gte');
  5614.             }
  5615.             
  5616.             return false;
  5617.         }
  5618.     };
  5619. });/**
  5620.  * Utility functions to work with <code>AbbreviationNode</code> as HTML element
  5621.  * @param {Function} require
  5622.  * @param {Underscore} _
  5623.  */
  5624. emmet.define('abbreviationUtils', function(require, _) {
  5625.     return {
  5626.         /**
  5627.          * Check if passed abbreviation node has matched snippet resource
  5628.          * @param {AbbreviationNode} node
  5629.          * @returns {Boolean}
  5630.          * @memberOf abbreviationUtils
  5631.          */
  5632.         isSnippet: function(node) {
  5633.             return require('elements').is(node.matchedResource(), 'snippet');
  5634.         },
  5635.         
  5636.         /**
  5637.          * Test if passed node is unary (no closing tag)
  5638.          * @param {AbbreviationNode} node
  5639.          * @return {Boolean}
  5640.          */
  5641.         isUnary: function(node) {
  5642.             var r = node.matchedResource();
  5643.             if (node.children.length || this.isSnippet(node))
  5644.                 return false;
  5645.             
  5646.             return r && r.is_empty || require('tagName').isEmptyElement(node.name());
  5647.         },
  5648.         
  5649.         /**
  5650.          * Test if passed node is inline-level (like <strong>, <img>)
  5651.          * @param {AbbreviationNode} node
  5652.          * @return {Boolean}
  5653.          */
  5654.         isInline: function(node) {
  5655.             return node.isTextNode() 
  5656.                 || !node.name() 
  5657.                 || require('tagName').isInlineLevel(node.name());
  5658.         },
  5659.         
  5660.         /**
  5661.          * Test if passed node is block-level
  5662.          * @param {AbbreviationNode} node
  5663.          * @return {Boolean}
  5664.          */
  5665.         isBlock: function(node) {
  5666.             return this.isSnippet(node) || !this.isInline(node);
  5667.         },
  5668.         
  5669.         /**
  5670.          * Test if given node is a snippet
  5671.          * @param {AbbreviationNode} node
  5672.          * @return {Boolean}
  5673.          */
  5674.         isSnippet: function(node) {
  5675.             return require('elements').is(node.matchedResource(), 'snippet');
  5676.         },
  5677.         
  5678.         /**
  5679.          * This function tests if passed node content contains HTML tags. 
  5680.          * This function is mostly used for output formatting
  5681.          * @param {AbbreviationNode} node
  5682.          * @returns {Boolean}
  5683.          */
  5684.         hasTagsInContent: function(node) {
  5685.             return require('utils').matchesTag(node.content);
  5686.         },
  5687.         
  5688.         /**
  5689.          * Test if current element contains block-level children
  5690.          * @param {AbbreviationNode} node
  5691.          * @return {Boolean}
  5692.          */
  5693.         hasBlockChildren: function(node) {
  5694.             return (this.hasTagsInContent(node) && this.isBlock(node)) 
  5695.                 || _.any(node.children, function(child) {
  5696.                     return this.isBlock(child);
  5697.                 }, this);
  5698.         },
  5699.         
  5700.         /**
  5701.          * Utility function that inserts content instead of <code>${child}</code>
  5702.          * variables on <code>text</code>
  5703.          * @param {String} text Text where child content should be inserted
  5704.          * @param {String} childContent Content to insert
  5705.          * @param {Object} options
  5706.          * @returns {String
  5707.          */
  5708.         insertChildContent: function(text, childContent, options) {
  5709.             options = _.extend({
  5710.                 keepVariable: true,
  5711.                 appendIfNoChild: true
  5712.             }, options || {});
  5713.             
  5714.             var childVariableReplaced = false;
  5715.             var utils = require('utils');
  5716.             text = utils.replaceVariables(text, function(variable, name, data) {
  5717.                 var output = variable;
  5718.                 if (name == 'child') {
  5719.                     // add correct indentation
  5720.                     output = utils.padString(childContent, utils.getLinePaddingFromPosition(text, data.start));
  5721.                     childVariableReplaced = true;
  5722.                     if (options.keepVariable)
  5723.                         output += variable;
  5724.                 }
  5725.                 
  5726.                 return output;
  5727.             });
  5728.             
  5729.             if (!childVariableReplaced && options.appendIfNoChild) {
  5730.                 text += childContent;
  5731.             }
  5732.             
  5733.             return text;
  5734.         }
  5735.     };
  5736. });/**
  5737.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  5738.  * @link http://chikuyonok.ru
  5739.  */
  5740. emmet.define('base64', function(require, _) {
  5741.     var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  5742.     
  5743.     return {
  5744.         /**
  5745.          * Encodes data using base64 algorithm
  5746.          * @author Tyler Akins (http://rumkin.com)
  5747.          * @param {String} input
  5748.          * @returns {String}
  5749.          * @memberOf emmet.base64
  5750.          */
  5751.         encode : function(input) {
  5752.             var output = [];
  5753.             var chr1, chr2, chr3, enc1, enc2, enc3, enc4, cdp1, cdp2, cdp3;
  5754.             var i = 0, il = input.length, b64 = chars;
  5755.  
  5756.             while (i < il) {
  5757.  
  5758.                 cdp1 = input.charCodeAt(i++);
  5759.                 cdp2 = input.charCodeAt(i++);
  5760.                 cdp3 = input.charCodeAt(i++);
  5761.  
  5762.                 chr1 = cdp1 & 0xff;
  5763.                 chr2 = cdp2 & 0xff;
  5764.                 chr3 = cdp3 & 0xff;
  5765.  
  5766.                 enc1 = chr1 >> 2;
  5767.                 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  5768.                 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  5769.                 enc4 = chr3 & 63;
  5770.  
  5771.                 if (isNaN(cdp2)) {
  5772.                     enc3 = enc4 = 64;
  5773.                 } else if (isNaN(cdp3)) {
  5774.                     enc4 = 64;
  5775.                 }
  5776.  
  5777.                 output.push(b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4));
  5778.             }
  5779.  
  5780.             return output.join('');
  5781.         },
  5782.  
  5783.         /**
  5784.          * Decodes string using MIME base64 algorithm
  5785.          * 
  5786.          * @author Tyler Akins (http://rumkin.com)
  5787.          * @param {String} data
  5788.          * @return {String}
  5789.          */
  5790.         decode : function(data) {
  5791.             var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmpArr = [];
  5792.             var b64 = chars, il = data.length;
  5793.  
  5794.             if (!data) {
  5795.                 return data;
  5796.             }
  5797.  
  5798.             data += '';
  5799.  
  5800.             do { // unpack four hexets into three octets using index points in b64
  5801.                 h1 = b64.indexOf(data.charAt(i++));
  5802.                 h2 = b64.indexOf(data.charAt(i++));
  5803.                 h3 = b64.indexOf(data.charAt(i++));
  5804.                 h4 = b64.indexOf(data.charAt(i++));
  5805.  
  5806.                 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
  5807.  
  5808.                 o1 = bits >> 16 & 0xff;
  5809.                 o2 = bits >> 8 & 0xff;
  5810.                 o3 = bits & 0xff;
  5811.  
  5812.                 if (h3 == 64) {
  5813.                     tmpArr[ac++] = String.fromCharCode(o1);
  5814.                 } else if (h4 == 64) {
  5815.                     tmpArr[ac++] = String.fromCharCode(o1, o2);
  5816.                 } else {
  5817.                     tmpArr[ac++] = String.fromCharCode(o1, o2, o3);
  5818.                 }
  5819.             } while (i < il);
  5820.  
  5821.             return tmpArr.join('');
  5822.         }
  5823.     };
  5824. });/**
  5825.  * HTML matcher: takes string and searches for HTML tag pairs for given position 
  5826.  * 
  5827.  * Unlike ΓÇ£classicΓÇ¥ matchers, it parses content from the specified 
  5828.  * position, not from the start, so it may work even outside HTML documents
  5829.  * (for example, inside strings of programming languages like JavaScript, Python 
  5830.  * etc.)
  5831.  * @constructor
  5832.  * @memberOf __htmlMatcherDefine
  5833.  */
  5834. emmet.define('htmlMatcher', function(require, _) {
  5835.     // Regular Expressions for parsing tags and attributes
  5836.     var reOpenTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
  5837.     var reCloseTag = /^<\/([\w\:\-]+)[^>]*>/;
  5838.     
  5839.     function openTag(i, match) {
  5840.         return {
  5841.             name: match[1],
  5842.             selfClose: !!match[3],
  5843.             /** @type Range */
  5844.             range: require('range').create(i, match[0]),
  5845.             type: 'open'
  5846.         };
  5847.     }
  5848.     
  5849.     function closeTag(i, match) {
  5850.         return {
  5851.             name: match[1],
  5852.             /** @type Range */
  5853.             range: require('range').create(i, match[0]),
  5854.             type: 'close'
  5855.         };
  5856.     }
  5857.     
  5858.     function comment(i, match) {
  5859.         return {
  5860.             /** @type Range */
  5861.             range: require('range').create(i, _.isNumber(match) ? match - i : match[0]),
  5862.             type: 'comment'
  5863.         };
  5864.     }
  5865.     
  5866.     /**
  5867.      * Creates new tag matcher session
  5868.      * @param {String} text
  5869.      */
  5870.     function createMatcher(text) {
  5871.         var memo = {}, m;
  5872.         return {
  5873.             /**
  5874.              * Test if given position matches opening tag
  5875.              * @param {Number} i
  5876.              * @returns {Object} Matched tag object
  5877.              */
  5878.             open: function(i) {
  5879.                 var m = this.matches(i);
  5880.                 return m && m.type == 'open' ? m : null;
  5881.             },
  5882.             
  5883.             /**
  5884.              * Test if given position matches closing tag
  5885.              * @param {Number} i
  5886.              * @returns {Object} Matched tag object
  5887.              */
  5888.             close: function(i) {
  5889.                 var m = this.matches(i);
  5890.                 return m && m.type == 'close' ? m : null;
  5891.             },
  5892.             
  5893.             /**
  5894.              * Matches either opening or closing tag for given position
  5895.              * @param i
  5896.              * @returns
  5897.              */
  5898.             matches: function(i) {
  5899.                 var key = 'p' + i;
  5900.                 
  5901.                 if (!(key in memo)) {
  5902.                     if (text.charAt(i) == '<') {
  5903.                         var substr = text.slice(i);
  5904.                         if (m = substr.match(reOpenTag)) {
  5905.                             memo[key] = openTag(i, m);
  5906.                         } else if (m = substr.match(reCloseTag)) {
  5907.                             memo[key] = closeTag(i, m);
  5908.                         } else {
  5909.                             // remember that given position contains no valid tag
  5910.                             memo[key] = false;
  5911.                         }
  5912.                     }
  5913.                 }
  5914.                 
  5915.                 return memo[key];
  5916.             },
  5917.             
  5918.             /**
  5919.              * Returns original text
  5920.              * @returns {String}
  5921.              */
  5922.             text: function() {
  5923.                 return text;
  5924.             }
  5925.         };
  5926.     }
  5927.     
  5928.     function matches(text, pos, pattern) {
  5929.         return text.substring(pos, pos + pattern.length) == pattern;
  5930.     }
  5931.     
  5932.     /**
  5933.      * Search for closing pair of opening tag
  5934.      * @param {Object} open Open tag instance
  5935.      * @param {Object} matcher Matcher instance
  5936.      */
  5937.     function findClosingPair(open, matcher) {
  5938.         var stack = [], tag = null;
  5939.         var text = matcher.text();
  5940.         
  5941.         for (var pos = open.range.end, len = text.length; pos < len; pos++) {
  5942.             if (matches(text, pos, '<!--')) {
  5943.                 // skip to end of comment
  5944.                 for (var j = pos; j < len; j++) {
  5945.                     if (matches(text, j, '-->')) {
  5946.                         pos = j + 3;
  5947.                         break;
  5948.                     }
  5949.                 }
  5950.             }
  5951.             
  5952.             if (tag = matcher.matches(pos)) {
  5953.                 if (tag.type == 'open' && !tag.selfClose) {
  5954.                     stack.push(tag.name);
  5955.                 } else if (tag.type == 'close') {
  5956.                     if (!stack.length) { // found valid pair?
  5957.                         return tag.name == open.name ? tag : null;
  5958.                     }
  5959.                     
  5960.                     // check if current closing tag matches previously opened one
  5961.                     if (_.last(stack) == tag.name) {
  5962.                         stack.pop();
  5963.                     } else {
  5964.                         var found = false;
  5965.                         while (stack.length && !found) {
  5966.                             var last = stack.pop();
  5967.                             if (last == tag.name) {
  5968.                                 found = true;
  5969.                             }
  5970.                         }
  5971.                         
  5972.                         if (!stack.length && !found) {
  5973.                             return tag.name == open.name ? tag : null;
  5974.                         }
  5975.                     }
  5976.                 }
  5977.             }
  5978.             
  5979.         }
  5980.     }
  5981.     
  5982.     return {
  5983.         /**
  5984.          * Main function: search for tag pair in <code>text</code> for given 
  5985.          * position
  5986.          * @memberOf htmlMatcher
  5987.          * @param {String} text 
  5988.          * @param {Number} pos
  5989.          * @returns {Object}
  5990.          */
  5991.         find: function(text, pos) {
  5992.             var range = require('range');
  5993.             var matcher = createMatcher(text); 
  5994.             var open = null, close = null;
  5995.             
  5996.             for (var i = pos; i >= 0; i--) {
  5997.                 if (open = matcher.open(i)) {
  5998.                     // found opening tag
  5999.                     if (open.selfClose && open.range.cmp(pos, 'lt', 'gt')) {
  6000.                         // inside self-closing tag
  6001.                         break;
  6002.                     }
  6003.                     
  6004.                     close = findClosingPair(open, matcher);
  6005.                     if (close) {
  6006.                         // found closing tag.
  6007.                         var r = range.create2(open.range.start, close.range.end);
  6008.                         if (r.contains(pos)) {
  6009.                             break;
  6010.                         }
  6011.                     } else if (open.range.contains(pos)) {
  6012.                         // we inside empty HTML tag like <br>
  6013.                         break;
  6014.                     }
  6015.                     
  6016.                     open = null;
  6017.                 } else if (matches(text, i, '-->')) {
  6018.                     // skip back to comment start
  6019.                     for (var j = i - 1; j >= 0; j--) {
  6020.                         if (matches(text, j, '-->')) {
  6021.                             // found another comment end, do nothing
  6022.                             break;
  6023.                         } else if (matches(text, j, '<!--')) {
  6024.                             i = j;
  6025.                             break;
  6026.                         }
  6027.                     }
  6028.                 } else if (matches(text, i, '<!--')) {
  6029.                     // we're inside comment, match it
  6030.                     var j = i + 4, jl = text.length;
  6031.                     for (; j < jl; j++) {
  6032.                         if (matches(text, j, '-->')) {
  6033.                             j += 3;
  6034.                             break;
  6035.                         }
  6036.                     }
  6037.                     
  6038.                     open = comment(i, j);
  6039.                     break;
  6040.                 }
  6041.             }
  6042.             
  6043.             if (open) {
  6044.                 var outerRange = null;
  6045.                 var innerRange = null;
  6046.                 
  6047.                 if (close) {
  6048.                     outerRange = range.create2(open.range.start, close.range.end);
  6049.                     innerRange = range.create2(open.range.end, close.range.start);
  6050.                 } else {
  6051.                     outerRange = innerRange = range.create2(open.range.start, open.range.end);
  6052.                 }
  6053.                 
  6054.                 if (open.type == 'comment') {
  6055.                     // adjust positions of inner range for comment
  6056.                     var _c = outerRange.substring(text);
  6057.                     innerRange.start += _c.length - _c.replace(/^<\!--\s*/, '').length;
  6058.                     innerRange.end -= _c.length - _c.replace(/\s*-->$/, '').length;
  6059.                 }
  6060.                 
  6061.                 return {
  6062.                     open: open,
  6063.                     close: close,
  6064.                     type: open.type == 'comment' ? 'comment' : 'tag',
  6065.                     innerRange: innerRange,
  6066.                     innerContent: function() {
  6067.                         return this.innerRange.substring(text);
  6068.                     },
  6069.                     outerRange: outerRange,
  6070.                     outerContent: function() {
  6071.                         return this.outerRange.substring(text);
  6072.                     },
  6073.                     range: !innerRange.length() || !innerRange.cmp(pos, 'lte', 'gte') ? outerRange : innerRange,
  6074.                     content: function() {
  6075.                         return this.range.substring(text);
  6076.                     },
  6077.                     source: text
  6078.                 };
  6079.             }
  6080.         },
  6081.         
  6082.         /**
  6083.          * The same as <code>find()</code> method, but restricts matched result 
  6084.          * to <code>tag</code> type
  6085.          * @param {String} text 
  6086.          * @param {Number} pos
  6087.          * @returns {Object}
  6088.          */
  6089.         tag: function(text, pos) {
  6090.             var result = this.find(text, pos);
  6091.             if (result && result.type == 'tag') {
  6092.                 return result;
  6093.             }
  6094.         }
  6095.     };
  6096. });/**
  6097.  * Utility module for handling tabstops tokens generated by Emmet's 
  6098.  * "Expand Abbreviation" action. The main <code>extract</code> method will take
  6099.  * raw text (for example: <i>${0} some ${1:text}</i>), find all tabstops 
  6100.  * occurrences, replace them with tokens suitable for your editor of choice and 
  6101.  * return object with processed text and list of found tabstops and their ranges.
  6102.  * For sake of portability (Objective-C/Java) the tabstops list is a plain 
  6103.  * sorted array with plain objects.
  6104.  * 
  6105.  * Placeholders with the same are meant to be <i>linked</i> in your editor.
  6106.  * @param {Function} require
  6107.  * @param {Underscore} _  
  6108.  */
  6109. emmet.define('tabStops', function(require, _) {
  6110.     /**
  6111.      * Global placeholder value, automatically incremented by 
  6112.      * <code>variablesResolver()</code> function
  6113.      */
  6114.     var startPlaceholderNum = 100;
  6115.     
  6116.     var tabstopIndex = 0;
  6117.     
  6118.     var defaultOptions = {
  6119.         replaceCarets: false,
  6120.         escape: function(ch) {
  6121.             return '\\' + ch;
  6122.         },
  6123.         tabstop: function(data) {
  6124.             return data.token;
  6125.         },
  6126.         variable: function(data) {
  6127.             return data.token;
  6128.         }
  6129.     };
  6130.     
  6131.     // XXX register output processor that will upgrade tabstops of parsed node
  6132.     // in order to prevent tabstop index conflicts
  6133.     require('abbreviationParser').addOutputProcessor(function(text, node, type) {
  6134.         var maxNum = 0;
  6135.         var tabstops = require('tabStops');
  6136.         var utils = require('utils');
  6137.         
  6138.         var tsOptions = {
  6139.             tabstop: function(data) {
  6140.                 var group = parseInt(data.group);
  6141.                 if (group == 0)
  6142.                     return '${0}';
  6143.                 
  6144.                 if (group > maxNum) maxNum = group;
  6145.                 if (data.placeholder) {
  6146.                     // respect nested placeholders
  6147.                     var ix = group + tabstopIndex;
  6148.                     var placeholder = tabstops.processText(data.placeholder, tsOptions);
  6149.                     return '${' + ix + ':' + placeholder + '}';
  6150.                 } else {
  6151.                     return '${' + (group + tabstopIndex) + '}';
  6152.                 }
  6153.             }
  6154.         };
  6155.         
  6156.         // upgrade tabstops
  6157.         text = tabstops.processText(text, tsOptions);
  6158.         
  6159.         // resolve variables
  6160.         text = utils.replaceVariables(text, tabstops.variablesResolver(node));
  6161.         
  6162.         tabstopIndex += maxNum + 1;
  6163.         return text;
  6164.     });
  6165.     
  6166.     return {
  6167.         /**
  6168.          * Main function that looks for a tabstops in provided <code>text</code>
  6169.          * and returns a processed version of <code>text</code> with expanded 
  6170.          * placeholders and list of tabstops found.
  6171.          * @param {String} text Text to process
  6172.          * @param {Object} options List of processor options:<br>
  6173.          * 
  6174.          * <b>replaceCarets</b> : <code>Boolean</code> ΓÇö replace all default
  6175.          * caret placeholders (like <i>{%::emmet-caret::%}</i>) with <i>${0:caret}</i><br>
  6176.          * 
  6177.          * <b>escape</b> : <code>Function</code> ΓÇö function that handle escaped
  6178.          * characters (mostly '$'). By default, it returns the character itself 
  6179.          * to be displayed as is in output, but sometimes you will use 
  6180.          * <code>extract</code> method as intermediate solution for further 
  6181.          * processing and want to keep character escaped. Thus, you should override
  6182.          * <code>escape</code> method to return escaped symbol (e.g. '\\$')<br>
  6183.          * 
  6184.          * <b>tabstop</b> : <code>Function</code> ΓÇô a tabstop handler. Receives 
  6185.          * a single argument ΓÇô an object describing token: its position, number 
  6186.          * group, placeholder and token itself. Should return a replacement 
  6187.          * string that will appear in final output
  6188.          * 
  6189.          * <b>variable</b> : <code>Function</code> ΓÇô variable handler. Receives 
  6190.          * a single argument ΓÇô an object describing token: its position, name 
  6191.          * and original token itself. Should return a replacement 
  6192.          * string that will appear in final output
  6193.          * 
  6194.          * @returns {Object} Object with processed <code>text</code> property
  6195.          * and array of <code>tabstops</code> found
  6196.          * @memberOf tabStops
  6197.          */
  6198.         extract: function(text, options) {
  6199.             // prepare defaults
  6200.             var utils = require('utils');
  6201.             var placeholders = {carets: ''};
  6202.             var marks = [];
  6203.             
  6204.             options = _.extend({}, defaultOptions, options, {
  6205.                 tabstop: function(data) {
  6206.                     var token = data.token;
  6207.                     var ret = '';
  6208.                     if (data.placeholder == 'cursor') {
  6209.                         marks.push({
  6210.                             start: data.start,
  6211.                             end: data.start + token.length,
  6212.                             group: 'carets',
  6213.                             value: ''
  6214.                         });
  6215.                     } else {
  6216.                         // unify placeholder value for single group
  6217.                         if ('placeholder' in data)
  6218.                             placeholders[data.group] = data.placeholder;
  6219.                         
  6220.                         if (data.group in placeholders)
  6221.                             ret = placeholders[data.group];
  6222.                         
  6223.                         marks.push({
  6224.                             start: data.start,
  6225.                             end: data.start + token.length,
  6226.                             group: data.group,
  6227.                             value: ret
  6228.                         });
  6229.                     }
  6230.                     
  6231.                     return token;
  6232.                 }
  6233.             });
  6234.             
  6235.             if (options.replaceCarets) {
  6236.                 text = text.replace(new RegExp( utils.escapeForRegexp( utils.getCaretPlaceholder() ), 'g'), '${0:cursor}');
  6237.             }
  6238.             
  6239.             // locate tabstops and unify group's placeholders
  6240.             text = this.processText(text, options);
  6241.             
  6242.             // now, replace all tabstops with placeholders
  6243.             var buf = utils.stringBuilder(), lastIx = 0;
  6244.             var tabStops = _.map(marks, function(mark) {
  6245.                 buf.append(text.substring(lastIx, mark.start));
  6246.                 
  6247.                 var pos = buf.length;
  6248.                 var ph = placeholders[mark.group] || '';
  6249.                 
  6250.                 buf.append(ph);
  6251.                 lastIx = mark.end;
  6252.                 
  6253.                 return {
  6254.                     group: mark.group,
  6255.                     start: pos,
  6256.                     end:  pos + ph.length
  6257.                 };
  6258.             });
  6259.             
  6260.             buf.append(text.substring(lastIx));
  6261.             
  6262.             return {
  6263.                 text: buf.toString(),
  6264.                 tabstops: _.sortBy(tabStops, 'start')
  6265.             };
  6266.         },
  6267.         
  6268.         /**
  6269.          * Text processing routine. Locates escaped characters and tabstops and
  6270.          * replaces them with values returned by handlers defined in 
  6271.          * <code>options</code>
  6272.          * @param {String} text
  6273.          * @param {Object} options See <code>extract</code> method options 
  6274.          * description
  6275.          * @returns {String}
  6276.          */
  6277.         processText: function(text, options) {
  6278.             options = _.extend({}, defaultOptions, options);
  6279.             
  6280.             var buf = require('utils').stringBuilder();
  6281.             /** @type StringStream */
  6282.             var stream = require('stringStream').create(text);
  6283.             var ch, m, a;
  6284.             
  6285.             while (ch = stream.next()) {
  6286.                 if (ch == '\\' && !stream.eol()) {
  6287.                     // handle escaped character
  6288.                     buf.append(options.escape(stream.next()));
  6289.                     continue;
  6290.                 }
  6291.                 
  6292.                 a = ch;
  6293.                 
  6294.                 if (ch == '$') {
  6295.                     // looks like a tabstop
  6296.                     stream.start = stream.pos - 1;
  6297.                     
  6298.                     if (m = stream.match(/^[0-9]+/)) {
  6299.                         // it's $N
  6300.                         a = options.tabstop({
  6301.                             start: buf.length, 
  6302.                             group: stream.current().substr(1),
  6303.                             token: stream.current()
  6304.                         });
  6305.                     } else if (m = stream.match(/^\{([a-z_\-][\w\-]*)\}/)) {
  6306.                         // ${variable}
  6307.                         a = options.variable({
  6308.                             start: buf.length, 
  6309.                             name: m[1],
  6310.                             token: stream.current()
  6311.                         });
  6312.                     } else if (m = stream.match(/^\{([0-9]+)(:.+?)?\}/, false)) {
  6313.                         // ${N:value} or ${N} placeholder
  6314.                         // parse placeholder, including nested ones
  6315.                         stream.skipToPair('{', '}');
  6316.                         
  6317.                         var obj = {
  6318.                             start: buf.length, 
  6319.                             group: m[1],
  6320.                             token: stream.current()
  6321.                         };
  6322.                         
  6323.                         var placeholder = obj.token.substring(obj.group.length + 2, obj.token.length - 1);
  6324.                         
  6325.                         if (placeholder) {
  6326.                             obj.placeholder = placeholder.substr(1);
  6327.                         }
  6328.                         
  6329.                         a = options.tabstop(obj);
  6330.                     }
  6331.                 }
  6332.                 
  6333.                 buf.append(a);
  6334.             }
  6335.             
  6336.             return buf.toString();
  6337.         },
  6338.         
  6339.         /**
  6340.          * Upgrades tabstops in output node in order to prevent naming conflicts
  6341.          * @param {AbbreviationNode} node
  6342.          * @param {Number} offset Tab index offset
  6343.          * @returns {Number} Maximum tabstop index in element
  6344.          */
  6345.         upgrade: function(node, offset) {
  6346.             var maxNum = 0;
  6347.             var options = {
  6348.                 tabstop: function(data) {
  6349.                     var group = parseInt(data.group);
  6350.                     if (group > maxNum) maxNum = group;
  6351.                         
  6352.                     if (data.placeholder)
  6353.                         return '${' + (group + offset) + ':' + data.placeholder + '}';
  6354.                     else
  6355.                         return '${' + (group + offset) + '}';
  6356.                 }
  6357.             };
  6358.             
  6359.             _.each(['start', 'end', 'content'], function(p) {
  6360.                 node[p] = this.processText(node[p], options);
  6361.             }, this);
  6362.             
  6363.             return maxNum;
  6364.         },
  6365.         
  6366.         /**
  6367.          * Helper function that produces a callback function for 
  6368.          * <code>replaceVariables()</code> method from {@link utils}
  6369.          * module. This callback will replace variable definitions (like 
  6370.          * ${var_name}) with their value defined in <i>resource</i> module,
  6371.          * or outputs tabstop with variable name otherwise.
  6372.          * @param {AbbreviationNode} node Context node
  6373.          * @returns {Function}
  6374.          */
  6375.         variablesResolver: function(node) {
  6376.             var placeholderMemo = {};
  6377.             var res = require('resources');
  6378.             return function(str, varName) {
  6379.                 // do not mark `child` variable as placeholder ΓÇô itΓÇÿs a reserved
  6380.                 // variable name
  6381.                 if (varName == 'child')
  6382.                     return str;
  6383.                 
  6384.                 if (varName == 'cursor')
  6385.                     return require('utils').getCaretPlaceholder();
  6386.                 
  6387.                 var attr = node.attribute(varName);
  6388.                 if (!_.isUndefined(attr))
  6389.                     return attr;
  6390.                 
  6391.                 var varValue = res.getVariable(varName);
  6392.                 if (varValue)
  6393.                     return varValue;
  6394.                 
  6395.                 // output as placeholder
  6396.                 if (!placeholderMemo[varName])
  6397.                     placeholderMemo[varName] = startPlaceholderNum++;
  6398.                     
  6399.                 return '${' + placeholderMemo[varName] + ':' + varName + '}';
  6400.             };
  6401.         },
  6402.         
  6403.         resetPlaceholderCounter: function() {
  6404.             console.log('deprecated');
  6405.             startPlaceholderNum = 100;
  6406.         },
  6407.         
  6408.         /**
  6409.          * Resets global tabstop index. When parsed tree is converted to output
  6410.          * string (<code>AbbreviationNode.toString()</code>), all tabstops 
  6411.          * defined in snippets and elements are upgraded in order to prevent
  6412.          * naming conflicts of nested. For example, <code>${1}</code> of a node
  6413.          * should not be linked with the same placehilder of the child node.
  6414.          * By default, <code>AbbreviationNode.toString()</code> automatically
  6415.          * upgrades tabstops of the same index for each node and writes maximum
  6416.          * tabstop index into the <code>tabstopIndex</code> variable. To keep
  6417.          * this variable at reasonable value, it is recommended to call 
  6418.          * <code>resetTabstopIndex()</code> method each time you expand variable 
  6419.          * @returns
  6420.          */
  6421.         resetTabstopIndex: function() {
  6422.             tabstopIndex = 0;
  6423.             startPlaceholderNum = 100;
  6424.         }
  6425.     };
  6426. });/**
  6427.  * Common module's preferences storage. This module 
  6428.  * provides general storage for all module preferences, their description and
  6429.  * default values.<br><br>
  6430.  * 
  6431.  * This module can also be used to list all available properties to create 
  6432.  * UI for updating properties
  6433.  * 
  6434.  * @memberOf __preferencesDefine
  6435.  * @constructor
  6436.  * @param {Function} require
  6437.  * @param {Underscore} _ 
  6438.  */
  6439. emmet.define('preferences', function(require, _) {
  6440.     var preferences = {};
  6441.     var defaults = {};
  6442.     var _dbgDefaults = null;
  6443.     var _dbgPreferences = null;
  6444.  
  6445.     function toBoolean(val) {
  6446.         if (_.isString(val)) {
  6447.             val = val.toLowerCase();
  6448.             return val == 'yes' || val == 'true' || val == '1';
  6449.         }
  6450.  
  6451.         return !!val;
  6452.     }
  6453.     
  6454.     function isValueObj(obj) {
  6455.         return _.isObject(obj) 
  6456.             && 'value' in obj 
  6457.             && _.keys(obj).length < 3;
  6458.     }
  6459.     
  6460.     return {
  6461.         /**
  6462.          * Creates new preference item with default value
  6463.          * @param {String} name Preference name. You can also pass object
  6464.          * with many options
  6465.          * @param {Object} value Preference default value
  6466.          * @param {String} description Item textual description
  6467.          * @memberOf preferences
  6468.          */
  6469.         define: function(name, value, description) {
  6470.             var prefs = name;
  6471.             if (_.isString(name)) {
  6472.                 prefs = {};
  6473.                 prefs[name] = {
  6474.                     value: value,
  6475.                     description: description
  6476.                 };
  6477.             }
  6478.             
  6479.             _.each(prefs, function(v, k) {
  6480.                 defaults[k] = isValueObj(v) ? v : {value: v};
  6481.             });
  6482.         },
  6483.         
  6484.         /**
  6485.          * Updates preference item value. Preference value should be defined
  6486.          * first with <code>define</code> method.
  6487.          * @param {String} name Preference name. You can also pass object
  6488.          * with many options
  6489.          * @param {Object} value Preference default value
  6490.          * @memberOf preferences
  6491.          */
  6492.         set: function(name, value) {
  6493.             var prefs = name;
  6494.             if (_.isString(name)) {
  6495.                 prefs = {};
  6496.                 prefs[name] = value;
  6497.             }
  6498.             
  6499.             _.each(prefs, function(v, k) {
  6500.                 if (!(k in defaults)) {
  6501.                     throw 'Property "' + k + '" is not defined. You should define it first with `define` method of current module';
  6502.                 }
  6503.                 
  6504.                 // do not set value if it equals to default value
  6505.                 if (v !== defaults[k].value) {
  6506.                     // make sure we have value of correct type
  6507.                     switch (typeof defaults[k].value) {
  6508.                         case 'boolean':
  6509.                             v = toBoolean(v);
  6510.                             break;
  6511.                         case 'number':
  6512.                             v = parseInt(v + '', 10) || 0;
  6513.                             break;
  6514.                         default: // convert to string
  6515.                             v += '';
  6516.                     }
  6517.  
  6518.                     preferences[k] = v;
  6519.                 } else if  (k in preferences) {
  6520.                     delete preferences[k];
  6521.                 }
  6522.             });
  6523.         },
  6524.         
  6525.         /**
  6526.          * Returns preference value
  6527.          * @param {String} name
  6528.          * @returns {String} Returns <code>undefined</code> if preference is 
  6529.          * not defined
  6530.          */
  6531.         get: function(name) {
  6532.             if (name in preferences)
  6533.                 return preferences[name];
  6534.             
  6535.             if (name in defaults)
  6536.                 return defaults[name].value;
  6537.             
  6538.             return void 0;
  6539.         },
  6540.         
  6541.         /**
  6542.          * Returns comma-separated preference value as array of values
  6543.          * @param {String} name
  6544.          * @returns {Array} Returns <code>undefined</code> if preference is 
  6545.          * not defined, <code>null</code> if string cannot be converted to array
  6546.          */
  6547.         getArray: function(name) {
  6548.             var val = this.get(name);
  6549.             if (!_.isUndefined(val)) {
  6550.                 val = _.map(val.split(','), require('utils').trim);
  6551.                 if (!val.length)
  6552.                     val = null;
  6553.             }
  6554.             
  6555.             return val;
  6556.         },
  6557.         
  6558.         /**
  6559.          * Returns comma and colon-separated preference value as dictionary
  6560.          * @param {String} name
  6561.          * @returns {Object}
  6562.          */
  6563.         getDict: function(name) {
  6564.             var result = {};
  6565.             _.each(this.getArray(name), function(val) {
  6566.                 var parts = val.split(':');
  6567.                 result[parts[0]] = parts[1];
  6568.             });
  6569.             
  6570.             return result;
  6571.         },
  6572.         
  6573.         /**
  6574.          * Returns description of preference item
  6575.          * @param {String} name Preference name
  6576.          * @returns {Object}
  6577.          */
  6578.         description: function(name) {
  6579.             return name in defaults ? defaults[name].description : void 0;
  6580.         },
  6581.         
  6582.         /**
  6583.          * Completely removes specified preference(s)
  6584.          * @param {String} name Preference name (or array of names)
  6585.          */
  6586.         remove: function(name) {
  6587.             if (!_.isArray(name))
  6588.                 name = [name];
  6589.             
  6590.             _.each(name, function(key) {
  6591.                 if (key in preferences)
  6592.                     delete preferences[key];
  6593.                 
  6594.                 if (key in defaults)
  6595.                     delete defaults[key];
  6596.             });
  6597.         },
  6598.         
  6599.         /**
  6600.          * Returns sorted list of all available properties
  6601.          * @returns {Array}
  6602.          */
  6603.         list: function() {
  6604.             return _.map(_.keys(defaults).sort(), function(key) {
  6605.                 return {
  6606.                     name: key,
  6607.                     value: this.get(key),
  6608.                     type: typeof defaults[key].value,
  6609.                     description: defaults[key].description
  6610.                 };
  6611.             }, this);
  6612.         },
  6613.         
  6614.         /**
  6615.          * Loads user-defined preferences from JSON
  6616.          * @param {Object} json
  6617.          * @returns
  6618.          */
  6619.         load: function(json) {
  6620.             _.each(json, function(value, key) {
  6621.                 this.set(key, value);
  6622.             }, this);
  6623.         },
  6624.  
  6625.         /**
  6626.          * Returns hash of user-modified preferences
  6627.          * @returns {Object}
  6628.          */
  6629.         exportModified: function() {
  6630.             return _.clone(preferences);
  6631.         },
  6632.         
  6633.         /**
  6634.          * Reset to defaults
  6635.          * @returns
  6636.          */
  6637.         reset: function() {
  6638.             preferences = {};
  6639.         },
  6640.         
  6641.         /**
  6642.          * For unit testing: use empty storage
  6643.          */
  6644.         _startTest: function() {
  6645.             _dbgDefaults = defaults;
  6646.             _dbgPreferences = preferences;
  6647.             defaults = {};
  6648.             preferences = {};
  6649.         },
  6650.         
  6651.         /**
  6652.          * For unit testing: restore original storage
  6653.          */
  6654.         _stopTest: function() {
  6655.             defaults = _dbgDefaults;
  6656.             preferences = _dbgPreferences;
  6657.         }
  6658.     };
  6659. });/**
  6660.  * Module for handling filters
  6661.  * @param {Function} require
  6662.  * @param {Underscore} _
  6663.  * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
  6664.  */
  6665. emmet.define('filters', function(require, _) {
  6666.     /** List of registered filters */
  6667.     var registeredFilters = {};
  6668.     
  6669.     /** Filters that will be applied for unknown syntax */
  6670.     var basicFilters = 'html';
  6671.     
  6672.     function list(filters) {
  6673.         if (!filters)
  6674.             return [];
  6675.         
  6676.         if (_.isString(filters))
  6677.             return filters.split(/[\|,]/g);
  6678.         
  6679.         return filters;
  6680.     }
  6681.     
  6682.     return  {
  6683.         /**
  6684.          * Register new filter
  6685.          * @param {String} name Filter name
  6686.          * @param {Function} fn Filter function
  6687.          */
  6688.         add: function(name, fn) {
  6689.             registeredFilters[name] = fn;
  6690.         },
  6691.         
  6692.         /**
  6693.          * Apply filters for final output tree
  6694.          * @param {AbbreviationNode} tree Output tree
  6695.          * @param {Array} filters List of filters to apply. Might be a 
  6696.          * <code>String</code>
  6697.          * @param {Object} profile Output profile, defined in <i>profile</i> 
  6698.          * module. Filters defined it profile are not used, <code>profile</code>
  6699.          * is passed to filter function
  6700.          * @memberOf emmet.filters
  6701.          * @returns {AbbreviationNode}
  6702.          */
  6703.         apply: function(tree, filters, profile) {
  6704.             var utils = require('utils');
  6705.             profile = require('profile').get(profile);
  6706.             
  6707.             _.each(list(filters), function(filter) {
  6708.                 var name = utils.trim(filter.toLowerCase());
  6709.                 if (name && name in registeredFilters) {
  6710.                     tree = registeredFilters[name](tree, profile);
  6711.                 }
  6712.             });
  6713.             
  6714.             return tree;
  6715.         },
  6716.         
  6717.         /**
  6718.          * Composes list of filters that should be applied to a tree, based on 
  6719.          * passed data
  6720.          * @param {String} syntax Syntax name ('html', 'css', etc.)
  6721.          * @param {Object} profile Output profile
  6722.          * @param {String} additionalFilters List or pipe-separated
  6723.          * string of additional filters to apply
  6724.          * @returns {Array}
  6725.          */
  6726.         composeList: function(syntax, profile, additionalFilters) {
  6727.             profile = require('profile').get(profile);
  6728.             var filters = list(profile.filters || require('resources').findItem(syntax, 'filters') || basicFilters);
  6729.             
  6730.             if (profile.extraFilters) {
  6731.                 filters = filters.concat(list(profile.extraFilters));
  6732.             }
  6733.                 
  6734.             if (additionalFilters) {
  6735.                 filters = filters.concat(list(additionalFilters));
  6736.             }
  6737.                 
  6738.             if (!filters || !filters.length) {
  6739.                 // looks like unknown syntax, apply basic filters
  6740.                 filters = list(basicFilters);
  6741.             }
  6742.                 
  6743.             return filters;
  6744.         },
  6745.         
  6746.         /**
  6747.          * Extracts filter list from abbreviation
  6748.          * @param {String} abbr
  6749.          * @returns {Array} Array with cleaned abbreviation and list of 
  6750.          * extracted filters
  6751.          */
  6752.         extractFromAbbreviation: function(abbr) {
  6753.             var filters = '';
  6754.             abbr = abbr.replace(/\|([\w\|\-]+)$/, function(str, p1){
  6755.                 filters = p1;
  6756.                 return '';
  6757.             });
  6758.             
  6759.             return [abbr, list(filters)];
  6760.         }
  6761.     };
  6762. });/**
  6763.  * Module that contains factories for element types used by Emmet
  6764.  * @param {Function} require
  6765.  * @param {Underscore} _
  6766.  */
  6767. emmet.define('elements', function(require, _) {
  6768.     var factories = {};
  6769.     var reAttrs = /([\w\-]+)\s*=\s*(['"])(.*?)\2/g;
  6770.     
  6771.     var result = {
  6772.         /**
  6773.          * Create new element factory
  6774.          * @param {String} name Element identifier
  6775.          * @param {Function} factory Function that produces element of specified 
  6776.          * type. The object generated by this factory is automatically 
  6777.          * augmented with <code>type</code> property pointing to element
  6778.          * <code>name</code>
  6779.          * @memberOf elements
  6780.          */
  6781.         add: function(name, factory) {
  6782.             var that = this;
  6783.             factories[name] = function() {
  6784.                 var elem = factory.apply(that, arguments);
  6785.                 if (elem)
  6786.                     elem.type = name;
  6787.                 
  6788.                 return elem;
  6789.             };
  6790.         },
  6791.         
  6792.         /**
  6793.          * Returns factory for specified name
  6794.          * @param {String} name
  6795.          * @returns {Function}
  6796.          */
  6797.         get: function(name) {
  6798.             return factories[name];
  6799.         },
  6800.         
  6801.         /**
  6802.          * Creates new element with specified type
  6803.          * @param {String} name
  6804.          * @returns {Object}
  6805.          */
  6806.         create: function(name) {
  6807.             var args = [].slice.call(arguments, 1);
  6808.             var factory = this.get(name);
  6809.             return factory ? factory.apply(this, args) : null;
  6810.         },
  6811.         
  6812.         /**
  6813.          * Check if passed element is of specified type
  6814.          * @param {Object} elem
  6815.          * @param {String} type
  6816.          * @returns {Boolean}
  6817.          */
  6818.         is: function(elem, type) {
  6819.             return elem && elem.type === type;
  6820.         }
  6821.     };
  6822.     
  6823.     // register resource references
  6824.     function commonFactory(value) {
  6825.         return {data: value};
  6826.     }
  6827.     
  6828.     /**
  6829.      * Element factory
  6830.      * @param {String} elementName Name of output element
  6831.      * @param {String} attrs Attributes definition. You may also pass
  6832.      * <code>Array</code> where each contains object with <code>name</code> 
  6833.      * and <code>value</code> properties, or <code>Object</code>
  6834.      * @param {Boolean} isEmpty Is expanded element should be empty
  6835.      */
  6836.     result.add('element', function(elementName, attrs, isEmpty) {
  6837.         var ret = {
  6838.             /** @memberOf __emmetDataElement */
  6839.             name: elementName,
  6840.             is_empty: !!isEmpty
  6841.         };
  6842.         
  6843.         if (attrs) {
  6844.             ret.attributes = [];
  6845.             if (_.isArray(attrs)) {
  6846.                 ret.attributes = attrs;
  6847.             } else if (_.isString(attrs)) {
  6848.                 var m;
  6849.                 while (m = reAttrs.exec(attrs)) {
  6850.                     ret.attributes.push({
  6851.                         name: m[1],
  6852.                         value: m[3]
  6853.                     });
  6854.                 }
  6855.             } else {
  6856.                 _.each(attrs, function(value, name) {
  6857.                     ret.attributes.push({
  6858.                         name: name, 
  6859.                         value: value
  6860.                     });
  6861.                 });
  6862.             }
  6863.         }
  6864.         
  6865.         return ret;
  6866.     });
  6867.     
  6868.     result.add('snippet', commonFactory);
  6869.     result.add('reference', commonFactory);
  6870.     result.add('empty', function() {
  6871.         return {};
  6872.     });
  6873.     
  6874.     return result;
  6875. });/**
  6876.  * Abstract implementation of edit tree interface.
  6877.  * Edit tree is a named container of editable ΓÇ£name-valueΓÇ¥ child elements, 
  6878.  * parsed from <code>source</code>. This container provides convenient methods
  6879.  * for editing/adding/removing child elements. All these update actions are
  6880.  * instantly reflected in the <code>source</code> code with respect of formatting.
  6881.  * <br><br>
  6882.  * For example, developer can create an edit tree from CSS rule and add or 
  6883.  * remove properties from itΓÇôall changes will be immediately reflected in the 
  6884.  * original source.
  6885.  * <br><br>
  6886.  * All classes defined in this module should be extended the same way as in
  6887.  * Backbone framework: using <code>extend</code> method to create new class and 
  6888.  * <code>initialize</code> method to define custom class constructor.
  6889.  * 
  6890.  * @example
  6891.  * <pre><code>
  6892.  * var MyClass = require('editTree').EditElement.extend({
  6893.  *     initialize: function() {
  6894.  *         // constructor code here
  6895.  *     }
  6896.  * });
  6897.  * 
  6898.  * var elem = new MyClass(); 
  6899.  * </code></pre>
  6900.  * 
  6901.  * 
  6902.  * @param {Function} require
  6903.  * @param {Underscore} _
  6904.  * @constructor
  6905.  * @memberOf __editTreeDefine
  6906.  */
  6907. emmet.define('editTree', function(require, _, core) {
  6908.     var range = require('range').create;
  6909.     
  6910.     /**
  6911.      * Named container of edited source
  6912.      * @type EditContainer
  6913.      * @param {String} source
  6914.      * @param {Object} options
  6915.      */
  6916.     function EditContainer(source, options) {
  6917.         this.options = _.extend({offset: 0}, options);
  6918.         /**
  6919.          * Source code of edited structure. All changes in the structure are 
  6920.          * immediately reflected into this property
  6921.          */
  6922.         this.source = source;
  6923.         
  6924.         /** 
  6925.          * List of all editable children
  6926.          * @private 
  6927.          */
  6928.         this._children = [];
  6929.         
  6930.         /**
  6931.          * Hash of all positions of container
  6932.          * @private
  6933.          */
  6934.         this._positions = {
  6935.             name: 0
  6936.         };
  6937.         
  6938.         this.initialize.apply(this, arguments);
  6939.     }
  6940.     
  6941.     /**
  6942.      * The self-propagating extend function for classes.
  6943.      * @type Function
  6944.      */
  6945.     EditContainer.extend = core.extend;
  6946.     
  6947.     EditContainer.prototype = {
  6948.         /**
  6949.          * Child class constructor
  6950.          */
  6951.         initialize: function() {},
  6952.         
  6953.         /**
  6954.          * Replace substring of tag's source
  6955.          * @param {String} value
  6956.          * @param {Number} start
  6957.          * @param {Number} end
  6958.          * @private
  6959.          */
  6960.         _updateSource: function(value, start, end) {
  6961.             // create modification range
  6962.             var r = range(start, _.isUndefined(end) ? 0 : end - start);
  6963.             var delta = value.length - r.length();
  6964.             
  6965.             var update = function(obj) {
  6966.                 _.each(obj, function(v, k) {
  6967.                     if (v >= r.end)
  6968.                         obj[k] += delta;
  6969.                 });
  6970.             };
  6971.             
  6972.             // update affected positions of current container
  6973.             update(this._positions);
  6974.             
  6975.             // update affected positions of children
  6976.             _.each(this.list(), function(item) {
  6977.                 update(item._positions);
  6978.             });
  6979.             
  6980.             this.source = require('utils').replaceSubstring(this.source, value, r);
  6981.         },
  6982.             
  6983.             
  6984.         /**
  6985.          * Adds new attribute 
  6986.          * @param {String} name Property name
  6987.          * @param {String} value Property value
  6988.          * @param {Number} pos Position at which to insert new property. By 
  6989.          * default the property is inserted at the end of rule 
  6990.          * @returns {EditElement} Newly created element
  6991.          */
  6992.         add: function(name, value, pos) {
  6993.             // this is abstract implementation
  6994.             var item = new EditElement(name, value);
  6995.             this._children.push(item);
  6996.             return item;
  6997.         },
  6998.         
  6999.         /**
  7000.          * Returns attribute object
  7001.          * @param {String} name Attribute name or its index
  7002.          * @returns {EditElement}
  7003.          */
  7004.         get: function(name) {
  7005.             if (_.isNumber(name))
  7006.                 return this.list()[name];
  7007.             
  7008.             if (_.isString(name))
  7009.                 return _.find(this.list(), function(prop) {
  7010.                     return prop.name() === name;
  7011.                 });
  7012.             
  7013.             return name;
  7014.         },
  7015.         
  7016.         /**
  7017.          * Returns all children by name or indexes
  7018.          * @param {Object} name Element name(s) or indexes (<code>String</code>,
  7019.          * <code>Array</code>, <code>Number</code>)
  7020.          * @returns {Array}
  7021.          */
  7022.         getAll: function(name) {
  7023.             if (!_.isArray(name))
  7024.                 name = [name];
  7025.             
  7026.             // split names and indexes
  7027.             var names = [], indexes = [];
  7028.             _.each(name, function(item) {
  7029.                 if (_.isString(item))
  7030.                     names.push(item);
  7031.                 else if (_.isNumber(item))
  7032.                     indexes.push(item);
  7033.             });
  7034.             
  7035.             return _.filter(this.list(), function(attribute, i) {
  7036.                 return _.include(indexes, i) || _.include(names, attribute.name());
  7037.             });
  7038.         },
  7039.         
  7040.         /**
  7041.          * Returns or updates element value. If such element doesn't exists,
  7042.          * it will be created automatically and added at the end of child list.
  7043.          * @param {String} name Element name or its index
  7044.          * @param {String} value New element value
  7045.          * @returns {String}
  7046.          */
  7047.         value: function(name, value, pos) {
  7048.             var element = this.get(name);
  7049.             if (element)
  7050.                 return element.value(value);
  7051.             
  7052.             if (!_.isUndefined(value)) {
  7053.                 // no such element ΓÇö create it
  7054.                 return this.add(name, value, pos);
  7055.             }
  7056.         },
  7057.         
  7058.         /**
  7059.          * Returns all values of child elements found by <code>getAll()</code>
  7060.          * method
  7061.          * @param {Object} name Element name(s) or indexes (<code>String</code>,
  7062.          * <code>Array</code>, <code>Number</code>)
  7063.          * @returns {Array}
  7064.          */
  7065.         values: function(name) {
  7066.             return _.map(this.getAll(name), function(element) {
  7067.                 return element.value();
  7068.             });
  7069.         },
  7070.         
  7071.         /**
  7072.          * Remove child element
  7073.          * @param {String} name Property name or its index
  7074.          */
  7075.         remove: function(name) {
  7076.             var element = this.get(name);
  7077.             if (element) {
  7078.                 this._updateSource('', element.fullRange());
  7079.                 this._children = _.without(this._children, element);
  7080.             }
  7081.         },
  7082.         
  7083.         /**
  7084.          * Returns list of all editable child elements
  7085.          * @returns {Array}
  7086.          */
  7087.         list: function() {
  7088.             return this._children;
  7089.         },
  7090.         
  7091.         /**
  7092.          * Returns index of editble child in list
  7093.          * @param {Object} item
  7094.          * @returns {Number}
  7095.          */
  7096.         indexOf: function(item) {
  7097.             return _.indexOf(this.list(), this.get(item));
  7098.         },
  7099.         
  7100.         /**
  7101.          * Sets or gets container name
  7102.          * @param {String} val New name. If not passed, current 
  7103.          * name is returned
  7104.          * @return {String}
  7105.          */
  7106.         name: function(val) {
  7107.             if (!_.isUndefined(val) && this._name !== (val = String(val))) {
  7108.                 this._updateSource(val, this._positions.name, this._positions.name + this._name.length);
  7109.                 this._name = val;
  7110.             }
  7111.             
  7112.             return this._name;
  7113.         },
  7114.         
  7115.         /**
  7116.          * Returns name range object
  7117.          * @param {Boolean} isAbsolute Return absolute range (with respect of 
  7118.          * rule offset)
  7119.          * @returns {Range}
  7120.          */
  7121.         nameRange: function(isAbsolute) {
  7122.             return range(this._positions.name + (isAbsolute ? this.options.offset : 0), this.name());
  7123.         },
  7124.         
  7125.         /**
  7126.          * Returns range of current source
  7127.          * @param {Boolean} isAbsolute
  7128.          */
  7129.         range: function(isAbsolute) {
  7130.             return range(isAbsolute ? this.options.offset : 0, this.toString());
  7131.         },
  7132.         
  7133.         /**
  7134.          * Returns element that belongs to specified position
  7135.          * @param {Number} pos
  7136.          * @param {Boolean} isAbsolute
  7137.          * @returns {EditElement}
  7138.          */
  7139.         itemFromPosition: function(pos, isAbsolute) {
  7140.             return _.find(this.list(), function(elem) {
  7141.                 return elem.range(isAbsolute).inside(pos);
  7142.             });
  7143.         },
  7144.         
  7145.         /**
  7146.          * Returns source code of current container 
  7147.          * @returns {String}
  7148.          */
  7149.         toString: function() {
  7150.             return this.source;
  7151.         }
  7152.     };
  7153.     
  7154.     /**
  7155.      * @param {EditContainer} parent
  7156.      * @param {Object} nameToken
  7157.      * @param {Object} valueToken
  7158.      */
  7159.     function EditElement(parent, nameToken, valueToken) {
  7160.         /** @type EditContainer */
  7161.         this.parent = parent;
  7162.         
  7163.         this._name = nameToken.value;
  7164.         this._value = valueToken ? valueToken.value : '';
  7165.         
  7166.         this._positions = {
  7167.             name: nameToken.start,
  7168.             value: valueToken ? valueToken.start : -1
  7169.         };
  7170.         
  7171.         this.initialize.apply(this, arguments);
  7172.     }
  7173.     
  7174.     /**
  7175.      * The self-propagating extend function for classes.
  7176.      * @type Function
  7177.      */
  7178.     EditElement.extend = core.extend;
  7179.     
  7180.     EditElement.prototype = {
  7181.         /**
  7182.          * Child class constructor
  7183.          */
  7184.         initialize: function() {},
  7185.         
  7186.         /**
  7187.          * Make position absolute
  7188.          * @private
  7189.          * @param {Number} num
  7190.          * @param {Boolean} isAbsolute
  7191.          * @returns {Boolean}
  7192.          */
  7193.         _pos: function(num, isAbsolute) {
  7194.             return num + (isAbsolute ? this.parent.options.offset : 0);
  7195.         },
  7196.             
  7197.         /**
  7198.          * Sets of gets element value
  7199.          * @param {String} val New element value. If not passed, current 
  7200.          * value is returned
  7201.          * @returns {String}
  7202.          */
  7203.         value: function(val) {
  7204.             if (!_.isUndefined(val) && this._value !== (val = String(val))) {
  7205.                 this.parent._updateSource(val, this.valueRange());
  7206.                 this._value = val;
  7207.             }
  7208.             
  7209.             return this._value;
  7210.         },
  7211.         
  7212.         /**
  7213.          * Sets of gets element name
  7214.          * @param {String} val New element name. If not passed, current 
  7215.          * name is returned
  7216.          * @returns {String}
  7217.          */
  7218.         name: function(val) {
  7219.             if (!_.isUndefined(val) && this._name !== (val = String(val))) {
  7220.                 this.parent._updateSource(val, this.nameRange());
  7221.                 this._name = val;
  7222.             }
  7223.             
  7224.             return this._name;
  7225.         },
  7226.         
  7227.         /**
  7228.          * Returns position of element name token
  7229.          * @param {Boolean} isAbsolute Return absolute position
  7230.          * @returns {Number}
  7231.          */
  7232.         namePosition: function(isAbsolute) {
  7233.             return this._pos(this._positions.name, isAbsolute);
  7234.         },
  7235.         
  7236.         /**
  7237.          * Returns position of element value token
  7238.          * @param {Boolean} isAbsolute Return absolute position
  7239.          * @returns {Number}
  7240.          */
  7241.         valuePosition: function(isAbsolute) {
  7242.             return this._pos(this._positions.value, isAbsolute);
  7243.         },
  7244.         
  7245.         /**
  7246.          * Returns element name
  7247.          * @param {Boolean} isAbsolute Return absolute range 
  7248.          * @returns {Range}
  7249.          */
  7250.         range: function(isAbsolute) {
  7251.             return range(this.namePosition(isAbsolute), this.toString());
  7252.         },
  7253.         
  7254.         /**
  7255.          * Returns full element range, including possible indentation
  7256.          * @param {Boolean} isAbsolute Return absolute range
  7257.          * @returns {Range}
  7258.          */
  7259.         fullRange: function(isAbsolute) {
  7260.             return this.range(isAbsolute);
  7261.         },
  7262.         
  7263.         /**
  7264.          * Returns element name range
  7265.          * @param {Boolean} isAbsolute Return absolute range
  7266.          * @returns {Range}
  7267.          */
  7268.         nameRange: function(isAbsolute) {
  7269.             return range(this.namePosition(isAbsolute), this.name());
  7270.         },
  7271.         
  7272.         /**
  7273.          * Returns element value range
  7274.          * @param {Boolean} isAbsolute Return absolute range
  7275.          * @returns {Range}
  7276.          */
  7277.         valueRange: function(isAbsolute) {
  7278.             return range(this.valuePosition(isAbsolute), this.value());
  7279.         },
  7280.         
  7281.         /**
  7282.          * Returns current element string representation
  7283.          * @returns {String}
  7284.          */
  7285.         toString: function() {
  7286.             return this.name() + this.value();
  7287.         },
  7288.         
  7289.         valueOf: function() {
  7290.             return this.toString();
  7291.         }
  7292.     };
  7293.     
  7294.     return {
  7295.         EditContainer: EditContainer,
  7296.         EditElement: EditElement,
  7297.         
  7298.         /**
  7299.          * Creates token that can be fed to <code>EditElement</code>
  7300.          * @param {Number} start
  7301.          * @param {String} value
  7302.          * @param {String} type
  7303.          * @returns
  7304.          */
  7305.         createToken: function(start, value, type) {
  7306.             var obj = {
  7307.                 start: start || 0,
  7308.                 value: value || '',
  7309.                 type: type
  7310.             };
  7311.             
  7312.             obj.end = obj.start + obj.value.length;
  7313.             return obj;
  7314.         }
  7315.     };
  7316. });/**
  7317.  * CSS EditTree is a module that can parse a CSS rule into a tree with 
  7318.  * convenient methods for adding, modifying and removing CSS properties. These 
  7319.  * changes can be written back to string with respect of code formatting.
  7320.  * 
  7321.  * @memberOf __cssEditTreeDefine
  7322.  * @constructor
  7323.  * @param {Function} require
  7324.  * @param {Underscore} _ 
  7325.  */
  7326. emmet.define('cssEditTree', function(require, _) {
  7327.     var defaultOptions = {
  7328.         styleBefore: '\n\t',
  7329.         styleSeparator: ': ',
  7330.         offset: 0
  7331.     };
  7332.     
  7333.     var WHITESPACE_REMOVE_FROM_START = 1;
  7334.     var WHITESPACE_REMOVE_FROM_END   = 2;
  7335.     
  7336.     /**
  7337.      * Returns range object
  7338.      * @param {Number} start
  7339.      * @param {Number} len 
  7340.      * @returns {Range}
  7341.      */
  7342.     function range(start, len) {
  7343.         return require('range').create(start, len);
  7344.     }
  7345.     
  7346.     /**
  7347.      * Removes whitespace tokens from the array ends
  7348.      * @param {Array} tokens
  7349.      * @param {Number} mask Mask indicating from which end whitespace should be 
  7350.      * removed 
  7351.      * @returns {Array}
  7352.      */
  7353.     function trimWhitespaceTokens(tokens, mask) {
  7354.         mask = mask || (WHITESPACE_REMOVE_FROM_START | WHITESPACE_REMOVE_FROM_END);
  7355.         var whitespace = ['white', 'line'];
  7356.         
  7357.         if ((mask & WHITESPACE_REMOVE_FROM_END) == WHITESPACE_REMOVE_FROM_END)
  7358.             while (tokens.length && _.include(whitespace, _.last(tokens).type)) {
  7359.                 tokens.pop();
  7360.              }
  7361.         
  7362.         if ((mask & WHITESPACE_REMOVE_FROM_START) == WHITESPACE_REMOVE_FROM_START)
  7363.             while (tokens.length && _.include(whitespace, tokens[0].type)) {
  7364.                 tokens.shift();
  7365.             }
  7366.         
  7367.         return tokens;
  7368.     }
  7369.     
  7370.     /**
  7371.      * Helper function that searches for selector range for <code>CSSEditRule</code>
  7372.      * @param {TokenIterator} it
  7373.      * @returns {Range}
  7374.      */
  7375.     function findSelectorRange(it) {
  7376.         var tokens = [], token;
  7377.          var start = it.position(), end;
  7378.          
  7379.          while (token = it.next()) {
  7380.             if (token.type == '{')
  7381.                 break;
  7382.             tokens.push(token);
  7383.         }
  7384.          
  7385.          trimWhitespaceTokens(tokens);
  7386.          
  7387.          if (tokens.length) {
  7388.              start = tokens[0].start;
  7389.              end = _.last(tokens).end;
  7390.          } else {
  7391.              end = start;
  7392.          }
  7393.          
  7394.          return range(start, end - start);
  7395.     }
  7396.     
  7397.     /**
  7398.      * Helper function that searches for CSS property value range next to
  7399.      * iterator's current position  
  7400.      * @param {TokenIterator} it
  7401.      * @returns {Range}
  7402.      */
  7403.     function findValueRange(it) {
  7404.         // find value start position
  7405.         var skipTokens = ['white', 'line', ':'];
  7406.         var tokens = [], token, start, end;
  7407.         
  7408.         it.nextUntil(function(tok) {
  7409.             return !_.include(skipTokens, this.itemNext().type);
  7410.         });
  7411.         
  7412.         start = it.current().end;
  7413.         // consume value
  7414.         while (token = it.next()) {
  7415.             if (token.type == '}' || token.type == ';') {
  7416.                 // found value end
  7417.                 trimWhitespaceTokens(tokens, WHITESPACE_REMOVE_FROM_START 
  7418.                         | (token.type == '}' ? WHITESPACE_REMOVE_FROM_END : 0));
  7419.                 
  7420.                 if (tokens.length) {
  7421.                     start = tokens[0].start;
  7422.                     end = _.last(tokens).end;
  7423.                 } else {
  7424.                     end = start;
  7425.                 }
  7426.                 
  7427.                 return range(start, end - start);
  7428.             }
  7429.             
  7430.             tokens.push(token);
  7431.         }
  7432.         
  7433.         // reached the end of tokens list
  7434.         if (tokens.length) {
  7435.             return range(tokens[0].start, _.last(tokens).end - tokens[0].start);
  7436.         }
  7437.     }
  7438.     
  7439.     /**
  7440.      * Finds parts of complex CSS value
  7441.      * @param {String} str
  7442.      * @returns {Array} Returns list of <code>Range</code>'s
  7443.      */
  7444.     function findParts(str) {
  7445.         /** @type StringStream */
  7446.         var stream = require('stringStream').create(str);
  7447.         var ch;
  7448.         var result = [];
  7449.         var sep = /[\s\u00a0,]/;
  7450.         
  7451.         var add = function() {
  7452.             stream.next();
  7453.             result.push(range(stream.start, stream.current()));
  7454.             stream.start = stream.pos;
  7455.         };
  7456.         
  7457.         // skip whitespace
  7458.         stream.eatSpace();
  7459.         stream.start = stream.pos;
  7460.         
  7461.         while (ch = stream.next()) {
  7462.             if (ch == '"' || ch == "'") {
  7463.                 stream.next();
  7464.                 if (!stream.skipTo(ch)) break;
  7465.                 add();
  7466.             } else if (ch == '(') {
  7467.                 // function found, may have nested function
  7468.                 stream.backUp(1);
  7469.                 if (!stream.skipToPair('(', ')')) break;
  7470.                 stream.backUp(1);
  7471.                 add();
  7472.             } else {
  7473.                 if (sep.test(ch)) {
  7474.                     result.push(range(stream.start, stream.current().length - 1));
  7475.                     stream.eatWhile(sep);
  7476.                     stream.start = stream.pos;
  7477.                 }
  7478.             }
  7479.         }
  7480.         
  7481.         add();
  7482.         
  7483.         return _.chain(result)
  7484.             .filter(function(item) {
  7485.                 return !!item.length();
  7486.             })
  7487.             .uniq(false, function(item) {
  7488.                 return item.toString();
  7489.             })
  7490.             .value();
  7491.     }
  7492.     
  7493.     /**
  7494.      * A bit hacky way to identify invalid CSS property definition: when user
  7495.      * starts writing new abbreviation in CSS rule, he actually creates invalid
  7496.      * CSS property definition and this method tries to identify such abbreviation
  7497.      * and prevent it from being added to CSS edit tree 
  7498.      * @param {TokenIterator} it
  7499.      */
  7500.     function isValidIdentifier(it) {
  7501. //        return true;
  7502.         var tokens = it.tokens;
  7503.         for (var i = it._i + 1, il = tokens.length; i < il; i++) {
  7504.             if (tokens[i].type == ':')
  7505.                 return true;
  7506.             
  7507.             if (tokens[i].type == 'identifier' || tokens[i].type == 'line')
  7508.                 return false;
  7509.         }
  7510.         
  7511.         return false;
  7512.     }
  7513.     
  7514.     /**
  7515.      * @class
  7516.      * @extends EditContainer
  7517.      */
  7518.     var CSSEditContainer = require('editTree').EditContainer.extend({
  7519.         initialize: function(source, options) {
  7520.             _.defaults(this.options, defaultOptions);
  7521.             var editTree = require('editTree');
  7522.             
  7523.             /** @type TokenIterator */
  7524.              var it = require('tokenIterator').create(
  7525.                      require('cssParser').parse(source));
  7526.              
  7527.              var selectorRange = findSelectorRange(it);
  7528.              this._positions.name = selectorRange.start;
  7529.              this._name = selectorRange.substring(source);
  7530.              
  7531.              if (!it.current() || it.current().type != '{')
  7532.                  throw 'Invalid CSS rule';
  7533.              
  7534.              this._positions.contentStart = it.position() + 1;
  7535.              
  7536.              // consume properties
  7537.              var propertyRange, valueRange, token;
  7538.             while (token = it.next()) {
  7539.                 if (token.type == 'identifier' && isValidIdentifier(it)) {
  7540.                     propertyRange = range(token);
  7541.                     valueRange = findValueRange(it);
  7542.                     var end = (it.current() && it.current().type == ';') 
  7543.                         ? range(it.current())
  7544.                         : range(valueRange.end, 0);
  7545.                     this._children.push(new CSSEditElement(this,
  7546.                             editTree.createToken(propertyRange.start, propertyRange.substring(source)),
  7547.                             editTree.createToken(valueRange.start, valueRange.substring(source)),
  7548.                             editTree.createToken(end.start, end.substring(source))
  7549.                             ));
  7550.                 }
  7551.             }
  7552.             
  7553.             this._saveStyle();
  7554.         },
  7555.         
  7556.         /**
  7557.          * Remembers all styles of properties
  7558.          * @private
  7559.          */
  7560.         _saveStyle: function() {
  7561.             var start = this._positions.contentStart;
  7562.             var source = this.source;
  7563.             var utils = require('utils');
  7564.             
  7565.             _.each(this.list(), /** @param {CSSEditProperty} p */ function(p) {
  7566.                 p.styleBefore = source.substring(start, p.namePosition());
  7567.                 // a small hack here:
  7568.                 // Sometimes users add empty lines before properties to logically
  7569.                 // separate groups of properties. In this case, a blind copy of
  7570.                 // characters between rules may lead to undesired behavior,
  7571.                 // especially when current rule is duplicated or used as a donor
  7572.                 // to create new rule.
  7573.                 // To solve this issue, weΓÇÿll take only last newline indentation
  7574.                 var lines = utils.splitByLines(p.styleBefore);
  7575.                 if (lines.length > 1) {
  7576.                     p.styleBefore = '\n' + _.last(lines);
  7577.                 }
  7578.                 
  7579.                 p.styleSeparator = source.substring(p.nameRange().end, p.valuePosition());
  7580.                 
  7581.                 // graceful and naive comments removal 
  7582.                 p.styleBefore = _.last(p.styleBefore.split('*/'));
  7583.                 p.styleSeparator = p.styleSeparator.replace(/\/\*.*?\*\//g, '');
  7584.                 
  7585.                 start = p.range().end;
  7586.             });
  7587.         },
  7588.         
  7589.         /**
  7590.          * Adds new CSS property 
  7591.          * @param {String} name Property name
  7592.          * @param {String} value Property value
  7593.          * @param {Number} pos Position at which to insert new property. By 
  7594.          * default the property is inserted at the end of rule 
  7595.          * @returns {CSSEditProperty}
  7596.          */
  7597.         add: function(name, value, pos) {
  7598.             var list = this.list();
  7599.             var start = this._positions.contentStart;
  7600.             var styles = _.pick(this.options, 'styleBefore', 'styleSeparator');
  7601.             var editTree = require('editTree');
  7602.             
  7603.             if (_.isUndefined(pos))
  7604.                 pos = list.length;
  7605.             
  7606.             /** @type CSSEditProperty */
  7607.             var donor = list[pos];
  7608.             if (donor) {
  7609.                 start = donor.fullRange().start;
  7610.             } else if (donor = list[pos - 1]) {
  7611.                 // make sure that donor has terminating semicolon
  7612.                 donor.end(';');
  7613.                 start = donor.range().end;
  7614.             }
  7615.             
  7616.             if (donor) {
  7617.                 styles = _.pick(donor, 'styleBefore', 'styleSeparator');
  7618.             }
  7619.             
  7620.             var nameToken = editTree.createToken(start + styles.styleBefore.length, name);
  7621.             var valueToken = editTree.createToken(nameToken.end + styles.styleSeparator.length, value);
  7622.             
  7623.             var property = new CSSEditElement(this, nameToken, valueToken,
  7624.                     editTree.createToken(valueToken.end, ';'));
  7625.             
  7626.             _.extend(property, styles);
  7627.             
  7628.             // write new property into the source
  7629.             this._updateSource(property.styleBefore + property.toString(), start);
  7630.             
  7631.             // insert new property
  7632.             this._children.splice(pos, 0, property);
  7633.             return property;
  7634.         }
  7635.     });
  7636.     
  7637.     /**
  7638.      * @class
  7639.      * @type CSSEditElement
  7640.      * @constructor
  7641.      */
  7642.     var CSSEditElement = require('editTree').EditElement.extend({
  7643.         initialize: function(rule, name, value, end) {
  7644.             this.styleBefore = rule.options.styleBefore;
  7645.             this.styleSeparator = rule.options.styleSeparator;
  7646.             
  7647.             this._end = end.value;
  7648.             this._positions.end = end.start;
  7649.         },
  7650.         
  7651.         /**
  7652.          * Returns ranges of complex value parts
  7653.          * @returns {Array} Returns <code>null</code> if value is not complex
  7654.          */
  7655.         valueParts: function(isAbsolute) {
  7656.             var parts = findParts(this.value());
  7657.             if (isAbsolute) {
  7658.                 var offset = this.valuePosition(true);
  7659.                 _.each(parts, function(p) {
  7660.                     p.shift(offset);
  7661.                 });
  7662.             }
  7663.             
  7664.             return parts;
  7665.         },
  7666.         
  7667.         /**
  7668.          * Sets of gets property end value (basically, it's a semicolon)
  7669.          * @param {String} val New end value. If not passed, current 
  7670.          * value is returned
  7671.          */
  7672.         end: function(val) {
  7673.             if (!_.isUndefined(val) && this._end !== val) {
  7674.                 this.parent._updateSource(val, this._positions.end, this._positions.end + this._end.length);
  7675.                 this._end = val;
  7676.             }
  7677.             
  7678.             return this._end;
  7679.         },
  7680.         
  7681.         /**
  7682.          * Returns full rule range, with indentation
  7683.          * @param {Boolean} isAbsolute Return absolute range (with respect of
  7684.          * rule offset)
  7685.          * @returns {Range}
  7686.          */
  7687.         fullRange: function(isAbsolute) {
  7688.             var r = this.range(isAbsolute);
  7689.             r.start -= this.styleBefore.length;
  7690.             return r;
  7691.         },
  7692.         
  7693.         /**
  7694.          * Returns item string representation
  7695.          * @returns {String}
  7696.          */
  7697.         toString: function() {
  7698.             return this.name() + this.styleSeparator + this.value() + this.end();
  7699.         }
  7700.     });
  7701.     
  7702.     return {
  7703.         /**
  7704.          * Parses CSS rule into editable tree
  7705.          * @param {String} source
  7706.          * @param {Object} options
  7707.          * @memberOf emmet.cssEditTree
  7708.          * @returns {EditContainer}
  7709.          */
  7710.         parse: function(source, options) {
  7711.             return new CSSEditContainer(source, options);
  7712.         },
  7713.         
  7714.         /**
  7715.          * Extract and parse CSS rule from specified position in <code>content</code> 
  7716.          * @param {String} content CSS source code
  7717.          * @param {Number} pos Character position where to start source code extraction
  7718.          * @returns {EditContainer}
  7719.          */
  7720.         parseFromPosition: function(content, pos, isBackward) {
  7721.             var bounds = this.extractRule(content, pos, isBackward);
  7722.             if (!bounds || !bounds.inside(pos))
  7723.                 // no matching CSS rule or caret outside rule bounds
  7724.                 return null;
  7725.             
  7726.             return this.parse(bounds.substring(content), {
  7727.                 offset: bounds.start
  7728.             });
  7729.         },
  7730.         
  7731.         /**
  7732.          * Extracts single CSS selector definition from source code
  7733.          * @param {String} content CSS source code
  7734.          * @param {Number} pos Character position where to start source code extraction
  7735.          * @returns {Range}
  7736.          */
  7737.         extractRule: function(content, pos, isBackward) {
  7738.             var result = '';
  7739.             var len = content.length;
  7740.             var offset = pos;
  7741.             var stopChars = '{}/\\<>\n\r';
  7742.             var bracePos = -1, ch;
  7743.             
  7744.             // search left until we find rule edge
  7745.             while (offset >= 0) {
  7746.                 ch = content.charAt(offset);
  7747.                 if (ch == '{') {
  7748.                     bracePos = offset;
  7749.                     break;
  7750.                 }
  7751.                 else if (ch == '}' && !isBackward) {
  7752.                     offset++;
  7753.                     break;
  7754.                 }
  7755.                 
  7756.                 offset--;
  7757.             }
  7758.             
  7759.             // search right for full rule set
  7760.             while (offset < len) {
  7761.                 ch = content.charAt(offset);
  7762.                 if (ch == '{') {
  7763.                     bracePos = offset;
  7764.                 } else if (ch == '}') {
  7765.                     if (bracePos != -1)
  7766.                         result = content.substring(bracePos, offset + 1);
  7767.                     break;
  7768.                 }
  7769.                 
  7770.                 offset++;
  7771.             }
  7772.             
  7773.             if (result) {
  7774.                 // find CSS selector
  7775.                 offset = bracePos - 1;
  7776.                 var selector = '';
  7777.                 while (offset >= 0) {
  7778.                     ch = content.charAt(offset);
  7779.                     if (stopChars.indexOf(ch) != -1) break;
  7780.                     offset--;
  7781.                 }
  7782.                 
  7783.                 // also trim whitespace
  7784.                 selector = content.substring(offset + 1, bracePos).replace(/^[\s\n\r]+/m, '');
  7785.                 return require('range').create(bracePos - selector.length, result.length + selector.length);
  7786.             }
  7787.             
  7788.             return null;
  7789.         },
  7790.         
  7791.         /**
  7792.           * Removes vendor prefix from CSS property
  7793.           * @param {String} name CSS property
  7794.           * @return {String}
  7795.           */
  7796.          baseName: function(name) {
  7797.              return name.replace(/^\s*\-\w+\-/, '');
  7798.          },
  7799.          
  7800.          /**
  7801.           * Finds parts of complex CSS value
  7802.           * @param {String} str
  7803.           * @returns {Array}
  7804.           */
  7805.          findParts: findParts
  7806.     };
  7807. });/**
  7808.  * XML EditTree is a module that can parse an XML/HTML element into a tree with 
  7809.  * convenient methods for adding, modifying and removing attributes. These 
  7810.  * changes can be written back to string with respect of code formatting.
  7811.  * 
  7812.  * @memberOf __xmlEditTreeDefine
  7813.  * @constructor
  7814.  * @param {Function} require
  7815.  * @param {Underscore} _ 
  7816.  */
  7817. emmet.define('xmlEditTree', function(require, _) {
  7818.     var defaultOptions = {
  7819.         styleBefore: ' ',
  7820.         styleSeparator: '=',
  7821.         styleQuote: '"',
  7822.         offset: 0
  7823.     };
  7824.     
  7825.     var startTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/m;
  7826.     
  7827.     var XMLEditContainer = require('editTree').EditContainer.extend({
  7828.         initialize: function(source, options) {
  7829.             _.defaults(this.options, defaultOptions);
  7830.             this._positions.name = 1;
  7831.             
  7832.             var attrToken = null;
  7833.             var tokens = require('xmlParser').parse(source);
  7834.             var range = require('range');
  7835.             
  7836.             _.each(tokens, function(token) {
  7837.                 token.value = range.create(token).substring(source);
  7838.                 switch (token.type) {
  7839.                     case 'tag':
  7840.                         if (/^<[^\/]+/.test(token.value)) {
  7841.                             this._name = token.value.substring(1);
  7842.                         }
  7843.                         break;
  7844.                         
  7845.                     case 'attribute':
  7846.                         // add empty attribute
  7847.                         if (attrToken) {
  7848.                             this._children.push(new XMLEditElement(this, attrToken));
  7849.                         }
  7850.                         
  7851.                         attrToken = token;
  7852.                         break;
  7853.                         
  7854.                     case 'string':
  7855.                         this._children.push(new XMLEditElement(this, attrToken, token));
  7856.                         attrToken = null;
  7857.                         break;
  7858.                 }
  7859.             }, this);
  7860.             
  7861.             if (attrToken) {
  7862.                 this._children.push(new XMLEditElement(this, attrToken));
  7863.             }
  7864.             
  7865.             this._saveStyle();
  7866.         },
  7867.         
  7868.         /**
  7869.          * Remembers all styles of properties
  7870.          * @private
  7871.          */
  7872.         _saveStyle: function() {
  7873.             var start = this.nameRange().end;
  7874.             var source = this.source;
  7875.             
  7876.             _.each(this.list(), /** @param {EditElement} p */ function(p) {
  7877.                 p.styleBefore = source.substring(start, p.namePosition());
  7878.                 
  7879.                 if (p.valuePosition() !== -1) {
  7880.                     p.styleSeparator = source.substring(p.namePosition() + p.name().length, p.valuePosition() - p.styleQuote.length);
  7881.                 }
  7882.                 
  7883.                 start = p.range().end;
  7884.             });
  7885.         },
  7886.         
  7887.         /**
  7888.          * Adds new attribute 
  7889.          * @param {String} name Property name
  7890.          * @param {String} value Property value
  7891.          * @param {Number} pos Position at which to insert new property. By 
  7892.          * default the property is inserted at the end of rule 
  7893.          */
  7894.         add: function(name, value, pos) {
  7895.             var list = this.list();
  7896.             var start = this.nameRange().end;
  7897.             var editTree = require('editTree');
  7898.             var styles = _.pick(this.options, 'styleBefore', 'styleSeparator', 'styleQuote');
  7899.             
  7900.             if (_.isUndefined(pos))
  7901.                 pos = list.length;
  7902.             
  7903.             
  7904.             /** @type XMLEditAttribute */
  7905.             var donor = list[pos];
  7906.             if (donor) {
  7907.                 start = donor.fullRange().start;
  7908.             } else if (donor = list[pos - 1]) {
  7909.                 start = donor.range().end;
  7910.             }
  7911.             
  7912.             if (donor) {
  7913.                 styles = _.pick(donor, 'styleBefore', 'styleSeparator', 'styleQuote');
  7914.             }
  7915.             
  7916.             value = styles.styleQuote + value + styles.styleQuote;
  7917.             
  7918.             var attribute = new XMLEditElement(this, 
  7919.                     editTree.createToken(start + styles.styleBefore.length, name),
  7920.                     editTree.createToken(start + styles.styleBefore.length + name.length 
  7921.                             + styles.styleSeparator.length, value)
  7922.                     );
  7923.             
  7924.             _.extend(attribute, styles);
  7925.             
  7926.             // write new attribute into the source
  7927.             this._updateSource(attribute.styleBefore + attribute.toString(), start);
  7928.             
  7929.             // insert new attribute
  7930.             this._children.splice(pos, 0, attribute);
  7931.             return attribute;
  7932.         }
  7933.     });
  7934.     
  7935.     var XMLEditElement = require('editTree').EditElement.extend({
  7936.         initialize: function(parent, nameToken, valueToken) {
  7937.             this.styleBefore = parent.options.styleBefore;
  7938.             this.styleSeparator = parent.options.styleSeparator;
  7939.             
  7940.             var value = '', quote = parent.options.styleQuote;
  7941.             if (valueToken) {
  7942.                 value = valueToken.value;
  7943.                 quote = value.charAt(0);
  7944.                 if (quote == '"' || quote == "'") {
  7945.                     value = value.substring(1);
  7946.                 } else {
  7947.                     quote = '';
  7948.                 }
  7949.                 
  7950.                 if (quote && value.charAt(value.length - 1) == quote) {
  7951.                     value = value.substring(0, value.length - 1);
  7952.                 }
  7953.             }
  7954.             
  7955.             this.styleQuote = quote;
  7956.             
  7957.             this._value = value;
  7958.             this._positions.value = valueToken ? valueToken.start + quote.length : -1;
  7959.         },
  7960.         
  7961.         /**
  7962.          * Returns full rule range, with indentation
  7963.          * @param {Boolean} isAbsolute Return absolute range (with respect of
  7964.          * rule offset)
  7965.          * @returns {Range}
  7966.          */
  7967.         fullRange: function(isAbsolute) {
  7968.             var r = this.range(isAbsolute);
  7969.             r.start -= this.styleBefore.length;
  7970.             return r;
  7971.         },
  7972.         
  7973.         toString: function() {
  7974.             return this.name() + this.styleSeparator
  7975.                 + this.styleQuote + this.value() + this.styleQuote;
  7976.         }
  7977.     });
  7978.     
  7979.     return {
  7980.         /**
  7981.          * Parses HTML element into editable tree
  7982.          * @param {String} source
  7983.          * @param {Object} options
  7984.          * @memberOf emmet.htmlEditTree
  7985.          * @returns {EditContainer}
  7986.          */
  7987.         parse: function(source, options) {
  7988.             return new XMLEditContainer(source, options);
  7989.         },
  7990.         
  7991.         /**
  7992.          * Extract and parse HTML from specified position in <code>content</code> 
  7993.          * @param {String} content CSS source code
  7994.          * @param {Number} pos Character position where to start source code extraction
  7995.          * @returns {XMLEditElement}
  7996.          */
  7997.         parseFromPosition: function(content, pos, isBackward) {
  7998.             var bounds = this.extractTag(content, pos, isBackward);
  7999.             if (!bounds || !bounds.inside(pos))
  8000.                 // no matching HTML tag or caret outside tag bounds
  8001.                 return null;
  8002.             
  8003.             return this.parse(bounds.substring(content), {
  8004.                 offset: bounds.start
  8005.             });
  8006.         },
  8007.         
  8008.         /**
  8009.          * Extracts nearest HTML tag range from <code>content</code>, starting at 
  8010.          * <code>pos</code> position
  8011.          * @param {String} content
  8012.          * @param {Number} pos
  8013.          * @param {Boolean} isBackward
  8014.          * @returns {Range}
  8015.          */
  8016.         extractTag: function(content, pos, isBackward) {
  8017.             var len = content.length, i;
  8018.             var range = require('range');
  8019.             
  8020.             // max extraction length. I don't think there may be tags larger 
  8021.             // than 2000 characters length
  8022.             var maxLen = Math.min(2000, len);
  8023.             
  8024.             /** @type Range */
  8025.             var r = null;
  8026.             
  8027.             var match = function(pos) {
  8028.                 var m;
  8029.                 if (content.charAt(pos) == '<' && (m = content.substr(pos, maxLen).match(startTag)))
  8030.                     return range.create(pos, m[0]);
  8031.             };
  8032.             
  8033.             // lookup backward, in case we are inside tag already
  8034.             for (i = pos; i >= 0; i--) {
  8035.                 if (r = match(i)) break;
  8036.             }
  8037.             
  8038.             if (r && (r.inside(pos) || isBackward))
  8039.                 return r;
  8040.             
  8041.             if (!r && isBackward)
  8042.                 return null;
  8043.             
  8044.             // search forward
  8045.             for (i = pos; i < len; i++) {
  8046.                 if (r = match(i))
  8047.                     return r;
  8048.             }
  8049.         }
  8050.     };
  8051. });/**
  8052.  * Filter for aiding of writing elements with complex class names as described
  8053.  * in Yandex's BEM (Block, Element, Modifier) methodology. This filter will
  8054.  * automatically inherit block and element names from parent elements and insert
  8055.  * them into child element classes
  8056.  * @memberOf __bemFilterDefine
  8057.  * @constructor
  8058.  * @param {Function} require
  8059.  * @param {Underscore} _
  8060.  */
  8061. emmet.exec(function(require, _) {
  8062.     var prefs = require('preferences');
  8063.     prefs.define('bem.elementSeparator', '__', 'Class nameΓÇÖs element separator.');
  8064.     prefs.define('bem.modifierSeparator', '_', 'Class nameΓÇÖs modifier separator.');
  8065.     prefs.define('bem.shortElementPrefix', '-', 
  8066.             'Symbol for describing short ΓÇ£block-elementΓÇ¥ notation. Class names '
  8067.             + 'prefixed with this symbol will be treated as element name for parentΓÇÿs '
  8068.             + 'block name. Each symbol instance traverses one level up in parsed ' 
  8069.             + 'tree for block name lookup. Empty value will disable short notation.');
  8070.     
  8071.     var shouldRunHtmlFilter = false;
  8072.     
  8073.     function getSeparators() {
  8074.         return {
  8075.             element: prefs.get('bem.elementSeparator'),
  8076.             modifier: prefs.get('bem.modifierSeparator')
  8077.         };
  8078.     }
  8079.     
  8080.     /**
  8081.      * @param {AbbreviationNode} item
  8082.      */
  8083.     function bemParse(item) {
  8084.         if (require('abbreviationUtils').isSnippet(item))
  8085.             return item;
  8086.         
  8087.         // save BEM stuff in cache for faster lookups
  8088.         item.__bem = {
  8089.             block: '',
  8090.             element: '',
  8091.             modifier: ''
  8092.         };
  8093.         
  8094.         var classNames = normalizeClassName(item.attribute('class')).split(' ');
  8095.         
  8096.         // guess best match for block name
  8097.         var reBlockName = /^[a-z]\-/i;
  8098.         item.__bem.block = _.find(classNames, function(name) {
  8099.             return reBlockName.test(name);
  8100.         });
  8101.         
  8102.         // guessing doesn't worked, pick first class name as block name
  8103.         if (!item.__bem.block) {
  8104.             reBlockName = /^[a-z]/i;
  8105.             item.__bem.block = _.find(classNames, function(name) {
  8106.                 return reBlockName.test(name);
  8107.             }) || '';
  8108.         }
  8109.         
  8110.         classNames = _.chain(classNames)
  8111.             .map(function(name) {return processClassName(name, item);})
  8112.             .flatten()
  8113.             .uniq()
  8114.             .value()
  8115.             .join(' ');
  8116.         
  8117.         if (classNames)
  8118.             item.attribute('class', classNames);
  8119.         
  8120.         return item;
  8121.     }
  8122.     
  8123.     /**
  8124.      * @param {String} className
  8125.      * @returns {String}
  8126.      */
  8127.     function normalizeClassName(className) {
  8128.         var utils = require('utils');
  8129.         className = (' ' + (className || '') + ' ').replace(/\s+/g, ' ');
  8130.         
  8131.         var shortSymbol = prefs.get('bem.shortElementPrefix');
  8132.         if (shortSymbol) {
  8133.             var re = new RegExp('\\s(' + utils.escapeForRegexp(shortSymbol) + '+)', 'g');
  8134.             className = className.replace(re, function(str, p1) {
  8135.                 return ' ' + utils.repeatString(getSeparators().element, p1.length);
  8136.             });
  8137.         }
  8138.         
  8139.         return utils.trim(className);
  8140.     }
  8141.     
  8142.     /**
  8143.      * Processes class name
  8144.      * @param {String} name Class name item to process
  8145.      * @param {AbbreviationNode} item Host node for provided class name
  8146.      * @returns Processed class name. May return <code>Array</code> of
  8147.      * class names 
  8148.      */
  8149.     function processClassName(name, item) {
  8150.         name = transformClassName(name, item, 'element');
  8151.         name = transformClassName(name, item, 'modifier');
  8152.         
  8153.         // expand class name
  8154.         // possible values:
  8155.         // * block__element
  8156.         // * block__element_modifier
  8157.         // * block__element_modifier1_modifier2
  8158.         // * block_modifier
  8159.         var block = '', element = '', modifier = '';
  8160.         var separators = getSeparators();
  8161.         if (~name.indexOf(separators.element)) {
  8162.             var blockElem = name.split(separators.element);
  8163.             var elemModifiers = blockElem[1].split(separators.modifier);
  8164.             
  8165.             block = blockElem[0];
  8166.             element = elemModifiers.shift();
  8167.             modifier = elemModifiers.join(separators.modifier);
  8168.         } else if (~name.indexOf(separators.modifier)) {
  8169.             var blockModifiers = name.split(separators.modifier);
  8170.             
  8171.             block = blockModifiers.shift();
  8172.             modifier = blockModifiers.join(separators.modifier);
  8173.         }
  8174.         
  8175.         if (block || element || modifier) {
  8176.             if (!block) {
  8177.                 block = item.__bem.block;
  8178.             }
  8179.             
  8180.             // inherit parent bem element, if exists
  8181. //            if (item.parent && item.parent.__bem && item.parent.__bem.element)
  8182. //                element = item.parent.__bem.element + separators.element + element;
  8183.             
  8184.             // produce multiple classes
  8185.             var prefix = block;
  8186.             var result = [];
  8187.             
  8188.             if (element) {
  8189.                 prefix += separators.element + element;
  8190.                 result.push(prefix);
  8191.             } else {
  8192.                 result.push(prefix);
  8193.             }
  8194.             
  8195.             if (modifier) {
  8196.                 result.push(prefix + separators.modifier + modifier);
  8197.             }
  8198.             
  8199.             item.__bem.block = block;
  8200.             item.__bem.element = element;
  8201.             item.__bem.modifier = modifier;
  8202.             
  8203.             return result;
  8204.         }
  8205.         
  8206.         // ...otherwise, return processed or original class name
  8207.         return name;
  8208.     }
  8209.     
  8210.     /**
  8211.      * Low-level function to transform user-typed class name into full BEM class
  8212.      * @param {String} name Class name item to process
  8213.      * @param {AbbreviationNode} item Host node for provided class name
  8214.      * @param {String} entityType Type of entity to be tried to transform 
  8215.      * ('element' or 'modifier')
  8216.      * @returns {String} Processed class name or original one if it can't be
  8217.      * transformed
  8218.      */
  8219.     function transformClassName(name, item, entityType) {
  8220.         var separators = getSeparators();
  8221.         var reSep = new RegExp('^(' + separators[entityType] + ')+', 'g');
  8222.         if (reSep.test(name)) {
  8223.             var depth = 0; // parent lookup depth
  8224.             var cleanName = name.replace(reSep, function(str, p1) {
  8225.                 depth = str.length / separators[entityType].length;
  8226.                 return '';
  8227.             });
  8228.             
  8229.             // find donor element
  8230.             var donor = item;
  8231.             while (donor.parent && depth--) {
  8232.                 donor = donor.parent;
  8233.             }
  8234.             
  8235.             if (!donor || !donor.__bem)
  8236.                 donor = item;
  8237.             
  8238.             if (donor && donor.__bem) {
  8239.                 var prefix = donor.__bem.block;
  8240.                 
  8241.                 // decide if we should inherit element name
  8242. //                if (entityType == 'element') {
  8243. //                    var curElem = cleanName.split(separators.modifier, 1)[0];
  8244. //                    if (donor.__bem.element && donor.__bem.element != curElem)
  8245. //                        prefix += separators.element + donor.__bem.element;
  8246. //                }
  8247.                 
  8248.                 if (entityType == 'modifier' &&  donor.__bem.element)
  8249.                     prefix += separators.element + donor.__bem.element;
  8250.                 
  8251.                 return prefix + separators[entityType] + cleanName;
  8252.             }
  8253.         }
  8254.         
  8255.         return name;
  8256.     }
  8257.     
  8258.     /**
  8259.      * Recursive function for processing tags, which extends class names 
  8260.      * according to BEM specs: http://bem.github.com/bem-method/pages/beginning/beginning.ru.html
  8261.      * <br><br>
  8262.      * It does several things:<br>
  8263.      * <ul>
  8264.      * <li>Expands complex class name (according to BEM symbol semantics):
  8265.      * .block__elem_modifier ΓåÆ .block.block__elem.block__elem_modifier
  8266.      * </li>
  8267.      * <li>Inherits block name on child elements: 
  8268.      * .b-block > .__el > .__el ΓåÆ .b-block > .b-block__el > .b-block__el__el
  8269.      * </li>
  8270.      * <li>Treats first dash symbol as '__'</li>
  8271.      * <li>Double underscore (or typographic 'ΓÇô') is also treated as an element 
  8272.      * level lookup, e.g. ____el will search for element definition in parentΓÇÖs 
  8273.      * parent element:
  8274.      * .b-block > .__el1 > .____el2 ΓåÆ .b-block > .b-block__el1 > .b-block__el2
  8275.      * </li>
  8276.      * </ul>
  8277.      * 
  8278.      * @param {AbbreviationNode} tree
  8279.      * @param {Object} profile
  8280.      */
  8281.     function process(tree, profile) {
  8282.         if (tree.name)
  8283.             bemParse(tree, profile);
  8284.         
  8285.         var abbrUtils = require('abbreviationUtils');
  8286.         _.each(tree.children, function(item) {
  8287.             process(item, profile);
  8288.             if (!abbrUtils.isSnippet(item) && item.start)
  8289.                 shouldRunHtmlFilter = true;
  8290.         });
  8291.         
  8292.         return tree;
  8293.     };
  8294.     
  8295.     require('filters').add('bem', function(tree, profile) {
  8296.         shouldRunHtmlFilter = false;
  8297.         tree = process(tree, profile);
  8298.         // in case 'bem' filter is applied after 'html' filter: run it again
  8299.         // to update output
  8300.         if (shouldRunHtmlFilter) {
  8301.             tree = require('filters').apply(tree, 'html', profile);
  8302.         }
  8303.         
  8304.         return tree;
  8305.     });
  8306. });
  8307.  
  8308. /**
  8309.  * Comment important tags (with 'id' and 'class' attributes)
  8310.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8311.  * @link http://chikuyonok.ru
  8312.  * @constructor
  8313.  * @memberOf __commentFilterDefine
  8314.  * @param {Function} require
  8315.  * @param {Underscore} _
  8316.  */
  8317. emmet.exec(function(require, _) {
  8318.     // define some preferences
  8319.     /** @type emmet.preferences */
  8320.     var prefs = require('preferences');
  8321.     
  8322.     prefs.define('filter.commentAfter', 
  8323.             '\n<!-- /<%= attr("id", "#") %><%= attr("class", ".") %> -->',
  8324.             'A definition of comment that should be placed <i>after</i> matched '
  8325.             + 'element when <code>comment</code> filter is applied. This definition '
  8326.             + 'is an ERB-style template passed to <code>_.template()</code> '
  8327.             + 'function (see Underscore.js docs for details). In template context, '
  8328.             + 'the following properties and functions are availabe:\n'
  8329.             + '<ul>'
  8330.             
  8331.             + '<li><code>attr(name, before, after)</code> ΓÇô a function that outputs' 
  8332.             + 'specified attribute value concatenated with <code>before</code> ' 
  8333.             + 'and <code>after</code> strings. If attribute doesn\'t exists, the ' 
  8334.             + 'empty string will be returned.</li>'
  8335.             
  8336.             + '<li><code>node</code> ΓÇô current node (instance of <code>AbbreviationNode</code>)</li>'
  8337.             
  8338.             + '<li><code>name</code> ΓÇô name of current tag</li>'
  8339.             
  8340.             + '<li><code>padding</code> ΓÇô current string padding, can be used ' 
  8341.             + 'for formatting</li>'
  8342.             
  8343.             +'</ul>');
  8344.     
  8345.     prefs.define('filter.commentBefore', 
  8346.             '',
  8347.             'A definition of comment that should be placed <i>before</i> matched '
  8348.             + 'element when <code>comment</code> filter is applied. '
  8349.             + 'For more info, read description of <code>filter.commentAfter</code> '
  8350.             + 'property');
  8351.     
  8352.     prefs.define('filter.commentTrigger', 'id, class',
  8353.             'A comma-separated list of attribute names that should exist in abbreviatoin '
  8354.             + 'where comment should be added. If you wish to add comment for '
  8355.             + 'every element, set this option to <code>*</code>');
  8356.     
  8357.     /**
  8358.      * Add comments to tag
  8359.      * @param {AbbreviationNode} node
  8360.      */
  8361.     function addComments(node, templateBefore, templateAfter) {
  8362.         var utils = require('utils');
  8363.         
  8364.         // check if comments should be added
  8365.         var trigger = prefs.get('filter.commentTrigger');
  8366.         if (trigger != '*') {
  8367.             var shouldAdd = _.find(trigger.split(','), function(name) {
  8368.                 return !!node.attribute(utils.trim(name));
  8369.             });
  8370.             if (!shouldAdd) return;
  8371.         }
  8372.         
  8373.         var ctx = {
  8374.             node: node,
  8375.             name: node.name(),
  8376.             padding: node.parent ? node.parent.padding : '',
  8377.             attr: function(name, before, after) {
  8378.                 var attr = node.attribute(name);
  8379.                 if (attr) {
  8380.                     return (before || '') + attr + (after || '');
  8381.                 }
  8382.                 
  8383.                 return '';
  8384.             }
  8385.         };
  8386.         
  8387.         var nodeBefore = utils.normalizeNewline(templateBefore ? templateBefore(ctx) : '');
  8388.         var nodeAfter = utils.normalizeNewline(templateAfter ? templateAfter(ctx) : '');
  8389.         
  8390.         node.start = node.start.replace(/</, nodeBefore + '<');
  8391.         node.end = node.end.replace(/>/, '>' + nodeAfter);
  8392.     }
  8393.     
  8394.     function process(tree, before, after) {
  8395.         var abbrUtils = require('abbreviationUtils');
  8396.         _.each(tree.children, function(item) {
  8397.             if (abbrUtils.isBlock(item))
  8398.                 addComments(item, before, after);
  8399.             
  8400.             process(item, before, after);
  8401.         });
  8402.             
  8403.         return tree;
  8404.     }
  8405.     
  8406.     require('filters').add('c', function(tree) {
  8407.         var templateBefore = _.template(prefs.get('filter.commentBefore'));
  8408.         var templateAfter = _.template(prefs.get('filter.commentAfter'));
  8409.         
  8410.         return process(tree, templateBefore, templateAfter);
  8411.     });
  8412. });
  8413. /**
  8414.  * Filter for escaping unsafe XML characters: <, >, &
  8415.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8416.  * @link http://chikuyonok.ru
  8417.  */
  8418. emmet.exec(function(require, _) {
  8419.     var charMap = {
  8420.         '<': '<',
  8421.         '>': '>',
  8422.         '&': '&'
  8423.     };
  8424.     
  8425.     function escapeChars(str) {
  8426.         return str.replace(/([<>&])/g, function(str, p1){
  8427.             return charMap[p1];
  8428.         });
  8429.     }
  8430.     
  8431.     require('filters').add('e', function process(tree) {
  8432.         _.each(tree.children, function(item) {
  8433.             item.start = escapeChars(item.start);
  8434.             item.end = escapeChars(item.end);
  8435.             item.content = escapeChars(item.content);
  8436.             process(item);
  8437.         });
  8438.         
  8439.         return tree;
  8440.     });
  8441. });/**
  8442.  * Generic formatting filter: creates proper indentation for each tree node,
  8443.  * placing "%s" placeholder where the actual output should be. You can use
  8444.  * this filter to preformat tree and then replace %s placeholder to whatever you
  8445.  * need. This filter should't be called directly from editor as a part 
  8446.  * of abbreviation.
  8447.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8448.  * @link http://chikuyonok.ru
  8449.  * @constructor
  8450.  * @memberOf __formatFilterDefine
  8451.  * @param {Function} require
  8452.  * @param {Underscore} _
  8453.  */
  8454. emmet.exec(function(require, _){
  8455.     var placeholder = '%s';
  8456.     
  8457.     function getIndentation() {
  8458.         return require('resources').getVariable('indentation');
  8459.     }
  8460.     
  8461.     /**
  8462.      * Test if passed node has block-level sibling element
  8463.      * @param {AbbreviationNode} item
  8464.      * @return {Boolean}
  8465.      */
  8466.     function hasBlockSibling(item) {
  8467.         return item.parent && require('abbreviationUtils').hasBlockChildren(item.parent);
  8468.     }
  8469.     
  8470.     /**
  8471.      * Test if passed item is very first child in parsed tree
  8472.      * @param {AbbreviationNode} item
  8473.      */
  8474.     function isVeryFirstChild(item) {
  8475.         return item.parent && !item.parent.parent && !item.index();
  8476.     }
  8477.     
  8478.     /**
  8479.      * Check if a newline should be added before element
  8480.      * @param {AbbreviationNode} node
  8481.      * @param {OutputProfile} profile
  8482.      * @return {Boolean}
  8483.      */
  8484.     function shouldAddLineBreak(node, profile) {
  8485.         var abbrUtils = require('abbreviationUtils');
  8486.         if (profile.tag_nl === true || abbrUtils.isBlock(node))
  8487.             return true;
  8488.         
  8489.         if (!node.parent || !profile.inline_break)
  8490.             return false;
  8491.         
  8492.         // check if there are required amount of adjacent inline element
  8493.         return shouldFormatInline(node.parent, profile);
  8494. }
  8495.     
  8496.     /**
  8497.      * Need to add newline because <code>item</code> has too many inline children
  8498.      * @param {AbbreviationNode} node
  8499.      * @param {OutputProfile} profile
  8500.      */
  8501.     function shouldBreakChild(node, profile) {
  8502.         // we need to test only one child element, because 
  8503.         // hasBlockChildren() method will do the rest
  8504.         return node.children.length && shouldAddLineBreak(node.children[0], profile);
  8505.     }
  8506.     
  8507.     function shouldFormatInline(node, profile) {
  8508.         var nodeCount = 0;
  8509.         var abbrUtils = require('abbreviationUtils');
  8510.         return !!_.find(node.children, function(child) {
  8511.             if (child.isTextNode() || !abbrUtils.isInline(child))
  8512.                 nodeCount = 0;
  8513.             else if (abbrUtils.isInline(child))
  8514.                 nodeCount++;
  8515.             
  8516.             if (nodeCount >= profile.inline_break)
  8517.                 return true;
  8518.         });
  8519.     }
  8520.     
  8521.     function isRoot(item) {
  8522.         return !item.parent;
  8523.     }
  8524.     
  8525.     /**
  8526.      * Processes element with matched resource of type <code>snippet</code>
  8527.      * @param {AbbreviationNode} item
  8528.      * @param {OutputProfile} profile
  8529.      * @param {Number} level Depth level
  8530.      */
  8531.     function processSnippet(item, profile, level) {
  8532.         item.start = item.end = '';
  8533.         if (!isVeryFirstChild(item) && profile.tag_nl !== false && shouldAddLineBreak(item, profile)) {
  8534.             // check if weΓÇÖre not inside inline element
  8535.             if (isRoot(item.parent) || !require('abbreviationUtils').isInline(item.parent)) {
  8536.                 item.start = require('utils').getNewline() + item.start;
  8537.             }
  8538.         }
  8539.         
  8540.         return item;
  8541.     }
  8542.     
  8543.     /**
  8544.      * Check if we should add line breaks inside inline element
  8545.      * @param {AbbreviationNode} node
  8546.      * @param {OutputProfile} profile
  8547.      * @return {Boolean}
  8548.      */
  8549.     function shouldBreakInsideInline(node, profile) {
  8550.         var abbrUtils = require('abbreviationUtils');
  8551.         var hasBlockElems = _.any(node.children, function(child) {
  8552.             if (abbrUtils.isSnippet(child))
  8553.                 return false;
  8554.             
  8555.             return !abbrUtils.isInline(child);
  8556.         });
  8557.         
  8558.         if (!hasBlockElems) {
  8559.             return shouldFormatInline(node, profile);
  8560.         }
  8561.         
  8562.         return true;
  8563.     }
  8564.     
  8565.     /**
  8566.      * Processes element with <code>tag</code> type
  8567.      * @param {AbbreviationNode} item
  8568.      * @param {OutputProfile} profile
  8569.      * @param {Number} level Depth level
  8570.      */
  8571.     function processTag(item, profile, level) {
  8572.         item.start = item.end = placeholder;
  8573.         var utils = require('utils');
  8574.         var abbrUtils = require('abbreviationUtils');
  8575.         var isUnary = abbrUtils.isUnary(item);
  8576.         var nl = utils.getNewline();
  8577.             
  8578.         // formatting output
  8579.         if (profile.tag_nl !== false) {
  8580.             var forceNl = profile.tag_nl === true && (profile.tag_nl_leaf || item.children.length);
  8581.             
  8582.             // formatting block-level elements
  8583.             if (!item.isTextNode()) {
  8584.                 if (shouldAddLineBreak(item, profile)) {
  8585.                     // - do not indent the very first element
  8586.                     // - do not indent first child of a snippet
  8587.                     if (!isVeryFirstChild(item) && (!abbrUtils.isSnippet(item.parent) || item.index()))
  8588.                         item.start = nl + item.start;
  8589.                         
  8590.                     if (abbrUtils.hasBlockChildren(item) || shouldBreakChild(item, profile) || (forceNl && !isUnary))
  8591.                         item.end = nl + item.end;
  8592.                         
  8593.                     if (abbrUtils.hasTagsInContent(item) || (forceNl && !item.children.length && !isUnary))
  8594.                         item.start += nl + getIndentation();
  8595.                 } else if (abbrUtils.isInline(item) && hasBlockSibling(item) && !isVeryFirstChild(item)) {
  8596.                     item.start = nl + item.start;
  8597.                 } else if (abbrUtils.isInline(item) && shouldBreakInsideInline(item, profile)) {
  8598.                     item.end = nl + item.end;
  8599.                 }
  8600.                 
  8601.                 item.padding = getIndentation() ;
  8602.             }
  8603.         }
  8604.         
  8605.         return item;
  8606.     }
  8607.     
  8608.     /**
  8609.      * Processes simplified tree, making it suitable for output as HTML structure
  8610.      * @param {AbbreviationNode} tree
  8611.      * @param {OutputProfile} profile
  8612.      * @param {Number} level Depth level
  8613.      */
  8614.     require('filters').add('_format', function process(tree, profile, level) {
  8615.         level = level || 0;
  8616.         var abbrUtils = require('abbreviationUtils');
  8617.         
  8618.         _.each(tree.children, function(item) {
  8619.             if (abbrUtils.isSnippet(item))
  8620.                 processSnippet(item, profile, level);
  8621.             else
  8622.                 processTag(item, profile, level);
  8623.             
  8624.             process(item, profile, level + 1);
  8625.         });
  8626.         
  8627.         return tree;
  8628.     });
  8629. });/**
  8630.  * Filter for producing HAML code from abbreviation.
  8631.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8632.  * @link http://chikuyonok.ru
  8633.  * @constructor
  8634.  * @memberOf __hamlFilterDefine
  8635.  * @param {Function} require
  8636.  * @param {Underscore} _
  8637.  */
  8638. emmet.exec(function(require, _) {
  8639.     var childToken = '${child}';
  8640.     
  8641.     function transformClassName(className) {
  8642.         return require('utils').trim(className).replace(/\s+/g, '.');
  8643.     }
  8644.     
  8645.     /**
  8646.      * Creates HAML attributes string from tag according to profile settings
  8647.      * @param {AbbreviationNode} tag
  8648.      * @param {Object} profile
  8649.      */
  8650.     function makeAttributesString(tag, profile) {
  8651.         var attrs = '';
  8652.         var otherAttrs = [];
  8653.         var attrQuote = profile.attributeQuote();
  8654.         var cursor = profile.cursor();
  8655.         
  8656.         _.each(tag.attributeList(), function(a) {
  8657.             var attrName = profile.attributeName(a.name);
  8658.             switch (attrName.toLowerCase()) {
  8659.                 // use short notation for ID and CLASS attributes
  8660.                 case 'id':
  8661.                     attrs += '#' + (a.value || cursor);
  8662.                     break;
  8663.                 case 'class':
  8664.                     attrs += '.' + transformClassName(a.value || cursor);
  8665.                     break;
  8666.                 // process other attributes
  8667.                 default:
  8668.                     otherAttrs.push(':' +attrName + ' => ' + attrQuote + (a.value || cursor) + attrQuote);
  8669.             }
  8670.         });
  8671.         
  8672.         if (otherAttrs.length)
  8673.             attrs += '{' + otherAttrs.join(', ') + '}';
  8674.         
  8675.         return attrs;
  8676.     }
  8677.     
  8678.     /**
  8679.      * Test if passed node has block-level sibling element
  8680.      * @param {AbbreviationNode} item
  8681.      * @return {Boolean}
  8682.      */
  8683.     function hasBlockSibling(item) {
  8684.         return item.parent && item.parent.hasBlockChildren();
  8685.     }
  8686.     
  8687.     /**
  8688.      * Processes element with <code>tag</code> type
  8689.      * @param {AbbreviationNode} item
  8690.      * @param {OutputProfile} profile
  8691.      * @param {Number} level Depth level
  8692.      */
  8693.     function processTag(item, profile, level) {
  8694.         if (!item.parent)
  8695.             // looks like it's root element
  8696.             return item;
  8697.         
  8698.         var abbrUtils = require('abbreviationUtils');
  8699.         var utils = require('utils');
  8700.         
  8701.         var attrs = makeAttributesString(item, profile);
  8702.         var cursor = profile.cursor();
  8703.         var isUnary = abbrUtils.isUnary(item);
  8704.         var selfClosing = profile.self_closing_tag && isUnary ? '/' : '';
  8705.         var start= '';
  8706.             
  8707.         // define tag name
  8708.         var tagName = '%' + profile.tagName(item.name());
  8709.         if (tagName.toLowerCase() == '%div' && attrs && attrs.indexOf('{') == -1)
  8710.             // omit div tag
  8711.             tagName = '';
  8712.             
  8713.         item.end = '';
  8714.         start = tagName + attrs + selfClosing + ' ';
  8715.         
  8716.         var placeholder = '%s';
  8717.         // We can't just replace placeholder with new value because
  8718.         // JavaScript will treat double $ character as a single one, assuming
  8719.         // we're using RegExp literal.
  8720.         item.start = utils.replaceSubstring(item.start, start, item.start.indexOf(placeholder), placeholder);
  8721.         
  8722.         if (!item.children.length && !isUnary)
  8723.             item.start += cursor;
  8724.         
  8725.         return item;
  8726.     }
  8727.     
  8728.     /**
  8729.      * Processes simplified tree, making it suitable for output as HTML structure
  8730.      * @param {AbbreviationNode} tree
  8731.      * @param {Object} profile
  8732.      * @param {Number} level Depth level
  8733.      */
  8734.     require('filters').add('haml', function process(tree, profile, level) {
  8735.         level = level || 0;
  8736.         var abbrUtils = require('abbreviationUtils');
  8737.         
  8738.         if (!level) {
  8739.             tree = require('filters').apply(tree, '_format', profile);
  8740.         }
  8741.         
  8742.         _.each(tree.children, function(item) {
  8743.             if (!abbrUtils.isSnippet(item))
  8744.                 processTag(item, profile, level);
  8745.             
  8746.             process(item, profile, level + 1);
  8747.         });
  8748.         
  8749.         return tree;
  8750.     });
  8751. });/**
  8752.  * Filter that produces HTML tree
  8753.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8754.  * @link http://chikuyonok.ru
  8755.  * @constructor
  8756.  * @memberOf __htmlFilterDefine
  8757.  * @param {Function} require
  8758.  * @param {Underscore} _
  8759.  */
  8760. emmet.exec(function(require, _) {
  8761.     /**
  8762.      * Creates HTML attributes string from tag according to profile settings
  8763.      * @param {AbbreviationNode} node
  8764.      * @param {OutputProfile} profile
  8765.      */
  8766.     function makeAttributesString(node, profile) {
  8767.         var attrQuote = profile.attributeQuote();
  8768.         var cursor = profile.cursor();
  8769.         
  8770.         return _.map(node.attributeList(), function(a) {
  8771.             var attrName = profile.attributeName(a.name);
  8772.             return ' ' + attrName + '=' + attrQuote + (a.value || cursor) + attrQuote;
  8773.         }).join('');
  8774.     }
  8775.     
  8776.     /**
  8777.      * Processes element with <code>tag</code> type
  8778.      * @param {AbbreviationNode} item
  8779.      * @param {OutputProfile} profile
  8780.      * @param {Number} level Depth level
  8781.      */
  8782.     function processTag(item, profile, level) {
  8783.         if (!item.parent) // looks like it's root element
  8784.             return item;
  8785.         
  8786.         var abbrUtils = require('abbreviationUtils');
  8787.         var utils = require('utils');
  8788.         
  8789.         var attrs = makeAttributesString(item, profile); 
  8790.         var cursor = profile.cursor();
  8791.         var isUnary = abbrUtils.isUnary(item);
  8792.         var start= '';
  8793.         var end = '';
  8794.             
  8795.         // define opening and closing tags
  8796.         if (!item.isTextNode()) {
  8797.             var tagName = profile.tagName(item.name());
  8798.             if (isUnary) {
  8799.                 start = '<' + tagName + attrs + profile.selfClosing() + '>';
  8800.                 item.end = '';
  8801.             } else {
  8802.                 start = '<' + tagName + attrs + '>';
  8803.                 end = '</' + tagName + '>';
  8804.             }
  8805.         }
  8806.         
  8807.         var placeholder = '%s';
  8808.         // We can't just replace placeholder with new value because
  8809.         // JavaScript will treat double $ character as a single one, assuming
  8810.         // we're using RegExp literal.
  8811.         item.start = utils.replaceSubstring(item.start, start, item.start.indexOf(placeholder), placeholder);
  8812.         item.end = utils.replaceSubstring(item.end, end, item.end.indexOf(placeholder), placeholder);
  8813.         
  8814.         if (!item.children.length && !isUnary && item.content.indexOf(cursor) == -1)
  8815.             item.start += cursor;
  8816.         
  8817.         return item;
  8818.     }
  8819.     
  8820.     /**
  8821.      * Processes simplified tree, making it suitable for output as HTML structure
  8822.      * @param {AbbreviationNode} tree
  8823.      * @param {Object} profile
  8824.      * @param {Number} level Depth level
  8825.      */
  8826.     require('filters').add('html', function process(tree, profile, level) {
  8827.         level = level || 0;
  8828.         var abbrUtils = require('abbreviationUtils');
  8829.         
  8830.         if (!level) {
  8831.             tree = require('filters').apply(tree, '_format', profile);
  8832.         }
  8833.         
  8834.         _.each(tree.children, function(item) {
  8835.             if (!abbrUtils.isSnippet(item))
  8836.                 processTag(item, profile, level);
  8837.             
  8838.             process(item, profile, level + 1);
  8839.         });
  8840.         
  8841.         return tree;
  8842.     });
  8843. });/**
  8844.  * Output abbreviation on a single line (i.e. no line breaks)
  8845.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8846.  * @link http://chikuyonok.ru
  8847.  * @constructor
  8848.  * @memberOf __singleLineFilterDefine
  8849.  * @param {Function} require
  8850.  * @param {Underscore} _
  8851.  */
  8852. emmet.exec(function(require, _) {
  8853.     var rePad = /^\s+/;
  8854.     var reNl = /[\n\r]/g;
  8855.     
  8856.     require('filters').add('s', function process(tree, profile, level) {
  8857.         var abbrUtils = require('abbreviationUtils');
  8858.         
  8859.         _.each(tree.children, function(item) {
  8860.             if (!abbrUtils.isSnippet(item)) {
  8861.                 // remove padding from item 
  8862.                 item.start = item.start.replace(rePad, '');
  8863.                 item.end = item.end.replace(rePad, '');
  8864.             }
  8865.             
  8866.             // remove newlines 
  8867.             item.start = item.start.replace(reNl, '');
  8868.             item.end = item.end.replace(reNl, '');
  8869.             item.content = item.content.replace(reNl, '');
  8870.             
  8871.             process(item);
  8872.         });
  8873.         
  8874.         return tree;
  8875.     });
  8876. });
  8877. /**
  8878.  * Trim filter: removes characters at the beginning of the text
  8879.  * content that indicates lists: numbers, #, *, -, etc.
  8880.  * 
  8881.  * Useful for wrapping lists with abbreviation.
  8882.  * 
  8883.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8884.  * @link http://chikuyonok.ru
  8885.  * 
  8886.  * @constructor
  8887.  * @memberOf __trimFilterDefine
  8888.  * @param {Function} require
  8889.  * @param {Underscore} _
  8890.  */
  8891. emmet.exec(function(require, _) {
  8892.     require('preferences').define('filter.trimRegexp', '[\\s|\\u00a0]*[\\d|#|\\-|\*|\\u2022]+\\.?\\s*',
  8893.             'Regular expression used to remove list markers (numbers, dashes, ' 
  8894.             + 'bullets, etc.) in <code>t</code> (trim) filter. The trim filter '
  8895.             + 'is useful for wrapping with abbreviation lists, pased from other ' 
  8896.             + 'documents (for example, Word documents).');
  8897.     
  8898.     function process(tree, re) {
  8899.         _.each(tree.children, function(item) {
  8900.             if (item.content)
  8901.                 item.content = item.content.replace(re, '');
  8902.             
  8903.             process(item, re);
  8904.         });
  8905.         
  8906.         return tree;
  8907.     }
  8908.     
  8909.     require('filters').add('t', function(tree) {
  8910.         var re = new RegExp(require('preferences').get('filter.trimRegexp'));
  8911.         return process(tree, re);
  8912.     });
  8913. });
  8914. /**
  8915.  * Filter for trimming "select" attributes from some tags that contains
  8916.  * child elements
  8917.  * @author Sergey Chikuyonok (serge.che@gmail.com)
  8918.  * @link http://chikuyonok.ru
  8919.  * 
  8920.  * @constructor
  8921.  * @memberOf __xslFilterDefine
  8922.  * @param {Function} require
  8923.  * @param {Underscore} _
  8924.  */
  8925. emmet.exec(function(require, _) {
  8926.     var tags = {
  8927.         'xsl:variable': 1,
  8928.         'xsl:with-param': 1
  8929.     };
  8930.     
  8931.     /**
  8932.      * Removes "select" attribute from node
  8933.      * @param {AbbreviationNode} node
  8934.      */
  8935.     function trimAttribute(node) {
  8936.         node.start = node.start.replace(/\s+select\s*=\s*(['"]).*?\1/, '');
  8937.     }
  8938.     
  8939.     require('filters').add('xsl', function process(tree) {
  8940.         var abbrUtils = require('abbreviationUtils');
  8941.         _.each(tree.children, function(item) {
  8942.             if (!abbrUtils.isSnippet(item)
  8943.                     && (item.name() || '').toLowerCase() in tags 
  8944.                     && item.children.length)
  8945.                 trimAttribute(item);
  8946.             process(item);
  8947.         });
  8948.         
  8949.         return tree;
  8950.     });
  8951. });/**
  8952.  * 'Expand abbreviation' editor action: extracts abbreviation from current caret 
  8953.  * position and replaces it with formatted output. 
  8954.  * <br><br>
  8955.  * This behavior can be overridden with custom handlers which can perform 
  8956.  * different actions when 'Expand Abbreviation' action is called.
  8957.  * For example, a CSS gradient handler that produces vendor-prefixed gradient
  8958.  * definitions registers its own expand abbreviation handler.  
  8959.  *  
  8960.  * @constructor
  8961.  * @memberOf __expandAbbreviationActionDefine
  8962.  * @param {Function} require
  8963.  * @param {Underscore} _
  8964.  */
  8965. emmet.define('expandAbbreviation', function(require, _) {
  8966.     /**
  8967.      * @type HandlerList List of registered handlers
  8968.      */
  8969.     var handlers = require('handlerList').create();
  8970.     
  8971.     /** Back-reference to module */
  8972.     var module = null;
  8973.     
  8974.     var actions = require('actions');
  8975.     /**
  8976.      * 'Expand abbreviation' editor action 
  8977.      * @param {IEmmetEditor} editor Editor instance
  8978.      * @param {String} syntax Syntax type (html, css, etc.)
  8979.      * @param {String} profile Output profile name (html, xml, xhtml)
  8980.      * @return {Boolean} Returns <code>true</code> if abbreviation was expanded 
  8981.      * successfully
  8982.      */
  8983.     actions.add('expand_abbreviation', function(editor, syntax, profile) {
  8984.         var args = _.toArray(arguments);
  8985.         
  8986.         // normalize incoming arguments
  8987.         var info = require('editorUtils').outputInfo(editor, syntax, profile);
  8988.         args[1] = info.syntax;
  8989.         args[2] = info.profile;
  8990.         
  8991.         return handlers.exec(false, args);
  8992.     });
  8993.     
  8994.     /**
  8995.      * A special version of <code>expandAbbreviation</code> function: if it can't
  8996.      * find abbreviation, it will place Tab character at caret position
  8997.      * @param {IEmmetEditor} editor Editor instance
  8998.      * @param {String} syntax Syntax type (html, css, etc.)
  8999.      * @param {String} profile Output profile name (html, xml, xhtml)
  9000.      */
  9001.     actions.add('expand_abbreviation_with_tab', function(editor, syntax, profile) {
  9002.         var sel = editor.getSelection();
  9003.         var indent = require('resources').getVariable('indentation');
  9004.         if (sel) {
  9005.             // indent selection
  9006.             var utils = require('utils');
  9007.             var selRange = require('range').create(editor.getSelectionRange());
  9008.             var content = utils.padString(sel, indent);
  9009.             
  9010.             editor.replaceContent(indent + '${0}', editor.getCaretPos());
  9011.             var replaceRange = require('range').create(editor.getCaretPos(), selRange.length());
  9012.             editor.replaceContent(content, replaceRange.start, replaceRange.end, true);
  9013.             editor.createSelection(replaceRange.start, replaceRange.start + content.length);
  9014.             return true;
  9015.         }
  9016.         
  9017.         if (!actions.run('expand_abbreviation', editor, syntax, profile)) {
  9018.             editor.replaceContent(indent, editor.getCaretPos());
  9019.         }
  9020.         
  9021.         return true;
  9022.     }, {hidden: true});
  9023.     
  9024.     // XXX setup default handler
  9025.     /**
  9026.      * Extracts abbreviation from current caret 
  9027.      * position and replaces it with formatted output 
  9028.      * @param {IEmmetEditor} editor Editor instance
  9029.      * @param {String} syntax Syntax type (html, css, etc.)
  9030.      * @param {String} profile Output profile name (html, xml, xhtml)
  9031.      * @return {Boolean} Returns <code>true</code> if abbreviation was expanded 
  9032.      * successfully
  9033.      */
  9034.     handlers.add(function(editor, syntax, profile) {
  9035.         var caretPos = editor.getSelectionRange().end;
  9036.         var abbr = module.findAbbreviation(editor);
  9037.             
  9038.         if (abbr) {
  9039.             var content = emmet.expandAbbreviation(abbr, syntax, profile, 
  9040.                     require('actionUtils').captureContext(editor));
  9041.             if (content) {
  9042.                 editor.replaceContent(content, caretPos - abbr.length, caretPos);
  9043.                 return true;
  9044.             }
  9045.         }
  9046.         
  9047.         return false;
  9048.     }, {order: -1});
  9049.     
  9050.     return module = {
  9051.         /**
  9052.          * Adds custom expand abbreviation handler. The passed function should 
  9053.          * return <code>true</code> if it was performed successfully, 
  9054.          * <code>false</code> otherwise.
  9055.          * 
  9056.          * Added handlers will be called when 'Expand Abbreviation' is called
  9057.          * in order they were added
  9058.          * @memberOf expandAbbreviation
  9059.          * @param {Function} fn
  9060.          * @param {Object} options
  9061.          */
  9062.         addHandler: function(fn, options) {
  9063.             handlers.add(fn, options);
  9064.         },
  9065.         
  9066.         /**
  9067.          * Removes registered handler
  9068.          * @returns
  9069.          */
  9070.         removeHandler: function(fn) {
  9071.             handlers.remove(fn, options);
  9072.         },
  9073.         
  9074.         /**
  9075.          * Search for abbreviation in editor from current caret position
  9076.          * @param {IEmmetEditor} editor Editor instance
  9077.          * @return {String}
  9078.          */
  9079.         findAbbreviation: function(editor) {
  9080.             /** @type Range */
  9081.             var range = require('range').create(editor.getSelectionRange());
  9082.             var content = String(editor.getContent());
  9083.             if (range.length()) {
  9084.                 // abbreviation is selected by user
  9085.                 return range.substring(content);
  9086.             }
  9087.             
  9088.             // search for new abbreviation from current caret position
  9089.             var curLine = editor.getCurrentLineRange();
  9090.             return require('actionUtils').extractAbbreviation(content.substring(curLine.start, range.start));
  9091.         }
  9092.     };
  9093. });/**
  9094.  * Action that wraps content with abbreviation. For convenience, action is 
  9095.  * defined as reusable module
  9096.  * @constructor
  9097.  * @memberOf __wrapWithAbbreviationDefine
  9098.  */
  9099. emmet.define('wrapWithAbbreviation', function(require, _) {
  9100.     /** Back-references to current module */
  9101.     var module = null;
  9102.     
  9103.     /**
  9104.      * Wraps content with abbreviation
  9105.      * @param {IEmmetEditor} Editor instance
  9106.      * @param {String} abbr Abbreviation to wrap with
  9107.      * @param {String} syntax Syntax type (html, css, etc.)
  9108.      * @param {String} profile Output profile name (html, xml, xhtml)
  9109.      */
  9110.     require('actions').add('wrap_with_abbreviation', function (editor, abbr, syntax, profile) {
  9111.         var info = require('editorUtils').outputInfo(editor, syntax, profile);
  9112.         var utils = require('utils');
  9113.         /** @type emmet.editorUtils */
  9114.         var editorUtils = require('editorUtils');
  9115.         abbr = abbr || editor.prompt("Enter abbreviation");
  9116.         
  9117.         if (!abbr) 
  9118.             return null;
  9119.         
  9120.         abbr = String(abbr);
  9121.         
  9122.         var range = require('range').create(editor.getSelectionRange());
  9123.         
  9124.         if (!range.length()) {
  9125.             // no selection, find tag pair
  9126.             var match = require('htmlMatcher').tag(info.content, range.start);
  9127.             if (!match) {  // nothing to wrap
  9128.                 return false;
  9129.             }
  9130.             
  9131.             range = utils.narrowToNonSpace(info.content, match.range);
  9132.         }
  9133.         
  9134.         var newContent = utils.escapeText(range.substring(info.content));
  9135.         var result = module
  9136.             .wrap(abbr, editorUtils.unindent(editor, newContent), info.syntax, 
  9137.                     info.profile, require('actionUtils').captureContext(editor));
  9138.         
  9139.         if (result) {
  9140.             editor.replaceContent(result, range.start, range.end);
  9141.             return true;
  9142.         }
  9143.         
  9144.         return false;
  9145.     });
  9146.     
  9147.     return module = {
  9148.         /**
  9149.          * Wraps passed text with abbreviation. Text will be placed inside last
  9150.          * expanded element
  9151.          * @memberOf wrapWithAbbreviation
  9152.          * @param {String} abbr Abbreviation
  9153.          * @param {String} text Text to wrap
  9154.          * @param {String} syntax Document type (html, xml, etc.). Default is 'html'
  9155.          * @param {String} profile Output profile's name. Default is 'plain'
  9156.          * @param {Object} contextNode Context node inside which abbreviation
  9157.          * is wrapped. It will be used as a reference for node name resolvers
  9158.          * @return {String}
  9159.          */
  9160.         wrap: function(abbr, text, syntax, profile, contextNode) {
  9161.             /** @type emmet.filters */
  9162.             var filters = require('filters');
  9163.             /** @type emmet.utils */
  9164.             var utils = require('utils');
  9165.             
  9166.             syntax = syntax || emmet.defaultSyntax();
  9167.             profile = require('profile').get(profile, syntax);
  9168.             
  9169.             require('tabStops').resetTabstopIndex();
  9170.             
  9171.             var data = filters.extractFromAbbreviation(abbr);
  9172.             var parsedTree = require('abbreviationParser').parse(data[0], {
  9173.                 syntax: syntax,
  9174.                 pastedContent: text,
  9175.                 contextNode: contextNode
  9176.             });
  9177.             if (parsedTree) {
  9178.                 var filtersList = filters.composeList(syntax, profile, data[1]);
  9179.                 filters.apply(parsedTree, filtersList, profile);
  9180.                 return utils.replaceVariables(parsedTree.toString());
  9181.             }
  9182.             
  9183.             return null;
  9184.         }
  9185.     };
  9186. });/**
  9187.  * Toggles HTML and CSS comments depending on current caret context. Unlike
  9188.  * the same action in most editors, this action toggles comment on currently
  9189.  * matched itemΓÇöHTML tag or CSS selectorΓÇöwhen nothing is selected.
  9190.  * 
  9191.  * @param {Function} require
  9192.  * @param {Underscore} _
  9193.  * @memberOf __toggleCommentAction
  9194.  * @constructor
  9195.  */
  9196. emmet.exec(function(require, _) {
  9197.     /**
  9198.      * Toggle HTML comment on current selection or tag
  9199.      * @param {IEmmetEditor} editor
  9200.      * @return {Boolean} Returns <code>true</code> if comment was toggled
  9201.      */
  9202.     function toggleHTMLComment(editor) {
  9203.         /** @type Range */
  9204.         var range = require('range').create(editor.getSelectionRange());
  9205.         var info = require('editorUtils').outputInfo(editor);
  9206.             
  9207.         if (!range.length()) {
  9208.             // no selection, find matching tag
  9209.             var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
  9210.             if (tag) { // found pair
  9211.                 range = tag.outerRange;
  9212.             }
  9213.         }
  9214.         
  9215.         return genericCommentToggle(editor, '<!--', '-->', range);
  9216.     }
  9217.  
  9218.     /**
  9219.      * Simple CSS commenting
  9220.      * @param {IEmmetEditor} editor
  9221.      * @return {Boolean} Returns <code>true</code> if comment was toggled
  9222.      */
  9223.     function toggleCSSComment(editor) {
  9224.         /** @type Range */
  9225.         var range = require('range').create(editor.getSelectionRange());
  9226.         var info = require('editorUtils').outputInfo(editor);
  9227.             
  9228.         if (!range.length()) {
  9229.             // no selection, try to get current rule
  9230.             /** @type CSSRule */
  9231.             var rule = require('cssEditTree').parseFromPosition(info.content, editor.getCaretPos());
  9232.             if (rule) {
  9233.                 var property = cssItemFromPosition(rule, editor.getCaretPos());
  9234.                 range = property 
  9235.                     ? property.range(true) 
  9236.                     : require('range').create(rule.nameRange(true).start, rule.source);
  9237.             }
  9238.         }
  9239.         
  9240.         if (!range.length()) {
  9241.             // still no selection, get current line
  9242.             range = require('range').create(editor.getCurrentLineRange());
  9243.             require('utils').narrowToNonSpace(info.content, range);
  9244.         }
  9245.         
  9246.         return genericCommentToggle(editor, '/*', '*/', range);
  9247.     }
  9248.     
  9249.     /**
  9250.      * Returns CSS property from <code>rule</code> that matches passed position
  9251.      * @param {EditContainer} rule
  9252.      * @param {Number} absPos
  9253.      * @returns {EditElement}
  9254.      */
  9255.     function cssItemFromPosition(rule, absPos) {
  9256.         // do not use default EditContainer.itemFromPosition() here, because
  9257.         // we need to make a few assumptions to make CSS commenting more reliable
  9258.         var relPos = absPos - (rule.options.offset || 0);
  9259.         var reSafeChar = /^[\s\n\r]/;
  9260.         return _.find(rule.list(), function(item) {
  9261.             if (item.range().end === relPos) {
  9262.                 // at the end of property, but outside of it
  9263.                 // if thereΓÇÖs a space character at current position,
  9264.                 // use current property
  9265.                 return reSafeChar.test(rule.source.charAt(relPos));
  9266.             }
  9267.             
  9268.             return item.range().inside(relPos);
  9269.         });
  9270.     }
  9271.  
  9272.     /**
  9273.      * Search for nearest comment in <code>str</code>, starting from index <code>from</code>
  9274.      * @param {String} text Where to search
  9275.      * @param {Number} from Search start index
  9276.      * @param {String} start_token Comment start string
  9277.      * @param {String} end_token Comment end string
  9278.      * @return {Range} Returns null if comment wasn't found
  9279.      */
  9280.     function searchComment(text, from, startToken, endToken) {
  9281.         var commentStart = -1;
  9282.         var commentEnd = -1;
  9283.         
  9284.         var hasMatch = function(str, start) {
  9285.             return text.substr(start, str.length) == str;
  9286.         };
  9287.             
  9288.         // search for comment start
  9289.         while (from--) {
  9290.             if (hasMatch(startToken, from)) {
  9291.                 commentStart = from;
  9292.                 break;
  9293.             }
  9294.         }
  9295.         
  9296.         if (commentStart != -1) {
  9297.             // search for comment end
  9298.             from = commentStart;
  9299.             var contentLen = text.length;
  9300.             while (contentLen >= from++) {
  9301.                 if (hasMatch(endToken, from)) {
  9302.                     commentEnd = from + endToken.length;
  9303.                     break;
  9304.                 }
  9305.             }
  9306.         }
  9307.         
  9308.         return (commentStart != -1 && commentEnd != -1) 
  9309.             ? require('range').create(commentStart, commentEnd - commentStart) 
  9310.             : null;
  9311.     }
  9312.  
  9313.     /**
  9314.      * Generic comment toggling routine
  9315.      * @param {IEmmetEditor} editor
  9316.      * @param {String} commentStart Comment start token
  9317.      * @param {String} commentEnd Comment end token
  9318.      * @param {Range} range Selection range
  9319.      * @return {Boolean}
  9320.      */
  9321.     function genericCommentToggle(editor, commentStart, commentEnd, range) {
  9322.         var editorUtils = require('editorUtils');
  9323.         var content = editorUtils.outputInfo(editor).content;
  9324.         var caretPos = editor.getCaretPos();
  9325.         var newContent = null;
  9326.         
  9327.         var utils = require('utils');
  9328.             
  9329.         /**
  9330.          * Remove comment markers from string
  9331.          * @param {Sting} str
  9332.          * @return {String}
  9333.          */
  9334.         function removeComment(str) {
  9335.             return str
  9336.                 .replace(new RegExp('^' + utils.escapeForRegexp(commentStart) + '\\s*'), function(str){
  9337.                     caretPos -= str.length;
  9338.                     return '';
  9339.                 }).replace(new RegExp('\\s*' + utils.escapeForRegexp(commentEnd) + '$'), '');
  9340.         }
  9341.         
  9342.         // first, we need to make sure that this substring is not inside 
  9343.         // comment
  9344.         var commentRange = searchComment(content, caretPos, commentStart, commentEnd);
  9345.         if (commentRange && commentRange.overlap(range)) {
  9346.             // we're inside comment, remove it
  9347.             range = commentRange;
  9348.             newContent = removeComment(range.substring(content));
  9349.         } else {
  9350.             // should add comment
  9351.             // make sure that there's no comment inside selection
  9352.             newContent = commentStart + ' ' +
  9353.                 range.substring(content)
  9354.                     .replace(new RegExp(utils.escapeForRegexp(commentStart) + '\\s*|\\s*' + utils.escapeForRegexp(commentEnd), 'g'), '') +
  9355.                 ' ' + commentEnd;
  9356.                 
  9357.             // adjust caret position
  9358.             caretPos += commentStart.length + 1;
  9359.         }
  9360.  
  9361.         // replace editor content
  9362.         if (newContent !== null) {
  9363.             newContent = utils.escapeText(newContent);
  9364.             editor.setCaretPos(range.start);
  9365.             editor.replaceContent(editorUtils.unindent(editor, newContent), range.start, range.end);
  9366.             editor.setCaretPos(caretPos);
  9367.             return true;
  9368.         }
  9369.         
  9370.         return false;
  9371.     }
  9372.     
  9373.     /**
  9374.      * Toggle comment on current editor's selection or HTML tag/CSS rule
  9375.      * @param {IEmmetEditor} editor
  9376.      */
  9377.     require('actions').add('toggle_comment', function(editor) {
  9378.         var info = require('editorUtils').outputInfo(editor);
  9379.         if (info.syntax == 'css') {
  9380.             // in case our editor is good enough and can recognize syntax from 
  9381.             // current token, we have to make sure that cursor is not inside
  9382.             // 'style' attribute of html element
  9383.             var caretPos = editor.getCaretPos();
  9384.             var tag = require('htmlMatcher').tag(info.content, caretPos);
  9385.             if (tag && tag.open.range.inside(caretPos)) {
  9386.                 info.syntax = 'html';
  9387.             }
  9388.         }
  9389.         
  9390.         if (info.syntax == 'css')
  9391.             return toggleCSSComment(editor);
  9392.         
  9393.         return toggleHTMLComment(editor);
  9394.     });
  9395. });/**
  9396.  * Move between next/prev edit points. 'Edit points' are places between tags 
  9397.  * and quotes of empty attributes in html
  9398.  * @constructor
  9399.  * 
  9400.  * @memberOf __editPointActionDefine
  9401.  * @param {Function} require
  9402.  * @param {Underscore} _
  9403.  */
  9404. emmet.exec(function(require, _) {
  9405.     /**
  9406.      * Search for new caret insertion point
  9407.      * @param {IEmmetEditor} editor Editor instance
  9408.      * @param {Number} inc Search increment: -1 ΓÇö search left, 1 ΓÇö search right
  9409.      * @param {Number} offset Initial offset relative to current caret position
  9410.      * @return {Number} Returns -1 if insertion point wasn't found
  9411.      */
  9412.     function findNewEditPoint(editor, inc, offset) {
  9413.         inc = inc || 1;
  9414.         offset = offset || 0;
  9415.         
  9416.         var curPoint = editor.getCaretPos() + offset;
  9417.         var content = String(editor.getContent());
  9418.         var maxLen = content.length;
  9419.         var nextPoint = -1;
  9420.         var reEmptyLine = /^\s+$/;
  9421.         
  9422.         function getLine(ix) {
  9423.             var start = ix;
  9424.             while (start >= 0) {
  9425.                 var c = content.charAt(start);
  9426.                 if (c == '\n' || c == '\r')
  9427.                     break;
  9428.                 start--;
  9429.             }
  9430.             
  9431.             return content.substring(start, ix);
  9432.         }
  9433.             
  9434.         while (curPoint <= maxLen && curPoint >= 0) {
  9435.             curPoint += inc;
  9436.             var curChar = content.charAt(curPoint);
  9437.             var nextChar = content.charAt(curPoint + 1);
  9438.             var prevChar = content.charAt(curPoint - 1);
  9439.                 
  9440.             switch (curChar) {
  9441.                 case '"':
  9442.                 case '\'':
  9443.                     if (nextChar == curChar && prevChar == '=') {
  9444.                         // empty attribute
  9445.                         nextPoint = curPoint + 1;
  9446.                     }
  9447.                     break;
  9448.                 case '>':
  9449.                     if (nextChar == '<') {
  9450.                         // between tags
  9451.                         nextPoint = curPoint + 1;
  9452.                     }
  9453.                     break;
  9454.                 case '\n':
  9455.                 case '\r':
  9456.                     // empty line
  9457.                     if (reEmptyLine.test(getLine(curPoint - 1))) {
  9458.                         nextPoint = curPoint;
  9459.                     }
  9460.                     break;
  9461.             }
  9462.             
  9463.             if (nextPoint != -1)
  9464.                 break;
  9465.         }
  9466.         
  9467.         return nextPoint;
  9468.     }
  9469.     
  9470.     /** @type emmet.actions */
  9471.     var actions = require('actions');
  9472.     
  9473.     /**
  9474.      * Move caret to previous edit point
  9475.      * @param {IEmmetEditor} editor Editor instance
  9476.      */
  9477.     actions.add('prev_edit_point', function(editor) {
  9478.         var curPos = editor.getCaretPos();
  9479.         var newPoint = findNewEditPoint(editor, -1);
  9480.             
  9481.         if (newPoint == curPos)
  9482.             // we're still in the same point, try searching from the other place
  9483.             newPoint = findNewEditPoint(editor, -1, -2);
  9484.         
  9485.         if (newPoint != -1) {
  9486.             editor.setCaretPos(newPoint);
  9487.             return true;
  9488.         }
  9489.         
  9490.         return false;
  9491.     }, {label: 'Previous Edit Point'});
  9492.     
  9493.     /**
  9494.      * Move caret to next edit point
  9495.      * @param {IEmmetEditor} editor Editor instance
  9496.      */
  9497.     actions.add('next_edit_point', function(editor) {
  9498.         var newPoint = findNewEditPoint(editor, 1);
  9499.         if (newPoint != -1) {
  9500.             editor.setCaretPos(newPoint);
  9501.             return true;
  9502.         }
  9503.         
  9504.         return false;
  9505.     });
  9506. });/**
  9507.  * Actions that use stream parsers and tokenizers for traversing:
  9508.  * -- Search for next/previous items in HTML
  9509.  * -- Search for next/previous items in CSS
  9510.  * @constructor
  9511.  * @memberOf __selectItemActionDefine
  9512.  * @param {Function} require
  9513.  * @param {Underscore} _
  9514.  */
  9515. emmet.exec(function(require, _) {
  9516.     var startTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
  9517.     
  9518.     /**
  9519.      * Generic function for searching for items to select
  9520.      * @param {IEmmetEditor} editor
  9521.      * @param {Boolean} isBackward Search backward (search forward otherwise)
  9522.      * @param {Function} extractFn Function that extracts item content
  9523.      * @param {Function} rangeFn Function that search for next token range
  9524.      */
  9525.     function findItem(editor, isBackward, extractFn, rangeFn) {
  9526.         var range = require('range');
  9527.         var content = require('editorUtils').outputInfo(editor).content;
  9528.         
  9529.         var contentLength = content.length;
  9530.         var itemRange, rng;
  9531.         /** @type Range */
  9532.         var prevRange = range.create(-1, 0);
  9533.         /** @type Range */
  9534.         var sel = range.create(editor.getSelectionRange());
  9535.         
  9536.         var searchPos = sel.start, loop = 100000; // endless loop protection
  9537.         while (searchPos >= 0 && searchPos < contentLength && --loop > 0) {
  9538.             if ( (itemRange = extractFn(content, searchPos, isBackward)) ) {
  9539.                 if (prevRange.equal(itemRange)) {
  9540.                     break;
  9541.                 }
  9542.                 
  9543.                 prevRange = itemRange.clone();
  9544.                 rng = rangeFn(itemRange.substring(content), itemRange.start, sel.clone());
  9545.                 
  9546.                 if (rng) {
  9547.                     editor.createSelection(rng.start, rng.end);
  9548.                     return true;
  9549.                 } else {
  9550.                     searchPos = isBackward ? itemRange.start : itemRange.end - 1;
  9551.                 }
  9552.             }
  9553.             
  9554.             searchPos += isBackward ? -1 : 1;
  9555.         }
  9556.         
  9557.         return false;
  9558.     }
  9559.     
  9560.     // XXX HTML section
  9561.     
  9562.     /**
  9563.      * Find next HTML item
  9564.      * @param {IEmmetEditor} editor
  9565.      */
  9566.     function findNextHTMLItem(editor) {
  9567.         var isFirst = true;
  9568.         return findItem(editor, false, function(content, searchPos){
  9569.             if (isFirst) {
  9570.                 isFirst = false;
  9571.                 return findOpeningTagFromPosition(content, searchPos);
  9572.             } else {
  9573.                 return getOpeningTagFromPosition(content, searchPos);
  9574.             }
  9575.         }, function(tag, offset, selRange) {
  9576.             return getRangeForHTMLItem(tag, offset, selRange, false);
  9577.         });
  9578.     }
  9579.     
  9580.     /**
  9581.      * Find previous HTML item
  9582.      * @param {IEmmetEditor} editor
  9583.      */
  9584.     function findPrevHTMLItem(editor) {
  9585.         return findItem(editor, true, getOpeningTagFromPosition, function (tag, offset, selRange) {
  9586.             return getRangeForHTMLItem(tag, offset, selRange, true);
  9587.         });
  9588.     }
  9589.     
  9590.     /**
  9591.      * Creates possible selection ranges for HTML tag
  9592.      * @param {String} source Original HTML source for tokens
  9593.      * @param {Array} tokens List of HTML tokens
  9594.      * @returns {Array}
  9595.      */
  9596.     function makePossibleRangesHTML(source, tokens, offset) {
  9597.         offset = offset || 0;
  9598.         var range = require('range');
  9599.         var result = [];
  9600.         var attrStart = -1, attrName = '', attrValue = '', attrValueRange, tagName;
  9601.         _.each(tokens, function(tok) {
  9602.             switch (tok.type) {
  9603.                 case 'tag':
  9604.                     tagName = source.substring(tok.start, tok.end);
  9605.                     if (/^<[\w\:\-]/.test(tagName)) {
  9606.                         // add tag name
  9607.                         result.push(range.create({
  9608.                             start: tok.start + 1, 
  9609.                             end: tok.end
  9610.                         }));
  9611.                     }
  9612.                     break;
  9613.                 case 'attribute':
  9614.                     attrStart = tok.start;
  9615.                     attrName = source.substring(tok.start, tok.end);
  9616.                     break;
  9617.                     
  9618.                 case 'string':
  9619.                     // attribute value
  9620.                     // push full attribute first
  9621.                      result.push(range.create(attrStart, tok.end - attrStart));
  9622.                      
  9623.                      attrValueRange = range.create(tok);
  9624.                      attrValue = attrValueRange.substring(source);
  9625.                      
  9626.                      // is this a quoted attribute?
  9627.                      if (isQuote(attrValue.charAt(0)))
  9628.                          attrValueRange.start++;
  9629.                      
  9630.                      if (isQuote(attrValue.charAt(attrValue.length - 1)))
  9631.                          attrValueRange.end--;
  9632.                      
  9633.                      result.push(attrValueRange);
  9634.                      
  9635.                      if (attrName == 'class') {
  9636.                          result = result.concat(classNameRanges(attrValueRange.substring(source), attrValueRange.start));
  9637.                      }
  9638.                      
  9639.                     break;
  9640.             }
  9641.         });
  9642.         
  9643.         // offset ranges
  9644.         _.each(result, function(r) {
  9645.             r.shift(offset);
  9646.         });
  9647.         
  9648.         return _.chain(result)
  9649.             .filter(function(item) {        // remove empty
  9650.                 return !!item.length();
  9651.             })
  9652.             .uniq(false, function(item) {   // remove duplicates
  9653.                 return item.toString();
  9654.             })
  9655.             .value();
  9656.     }
  9657.     
  9658.     /**
  9659.      * Returns ranges of class names in "class" attribute value
  9660.      * @param {String} className
  9661.      * @returns {Array}
  9662.      */
  9663.     function classNameRanges(className, offset) {
  9664.         offset = offset || 0;
  9665.         var result = [];
  9666.         /** @type StringStream */
  9667.         var stream = require('stringStream').create(className);
  9668.         var range = require('range');
  9669.         
  9670.         // skip whitespace
  9671.         stream.eatSpace();
  9672.         stream.start = stream.pos;
  9673.         
  9674.         var ch;
  9675.         while (ch = stream.next()) {
  9676.             if (/[\s\u00a0]/.test(ch)) {
  9677.                 result.push(range.create(stream.start + offset, stream.pos - stream.start - 1));
  9678.                 stream.eatSpace();
  9679.                 stream.start = stream.pos;
  9680.             }
  9681.         }
  9682.         
  9683.         result.push(range.create(stream.start + offset, stream.pos - stream.start));
  9684.         return result;
  9685.     }
  9686.     
  9687.     /**
  9688.      * Returns best HTML tag range match for current selection
  9689.      * @param {String} tag Tag declaration
  9690.      * @param {Number} offset Tag's position index inside content
  9691.      * @param {Range} selRange Selection range
  9692.      * @return {Range} Returns range if next item was found, <code>null</code> otherwise
  9693.      */
  9694.     function getRangeForHTMLItem(tag, offset, selRange, isBackward) {
  9695.         var ranges = makePossibleRangesHTML(tag, require('xmlParser').parse(tag), offset);
  9696.         
  9697.         if (isBackward)
  9698.             ranges.reverse();
  9699.         
  9700.         // try to find selected range
  9701.         var curRange = _.find(ranges, function(r) {
  9702.             return r.equal(selRange);
  9703.         });
  9704.         
  9705.         if (curRange) {
  9706.             var ix = _.indexOf(ranges, curRange);
  9707.             if (ix < ranges.length - 1)
  9708.                 return ranges[ix + 1];
  9709.             
  9710.             return null;
  9711.         }
  9712.         
  9713.         // no selected range, find nearest one
  9714.         if (isBackward)
  9715.             // search backward
  9716.             return _.find(ranges, function(r) {
  9717.                 return r.start < selRange.start;
  9718.             });
  9719.         
  9720.         // search forward
  9721.         // to deal with overlapping ranges (like full attribute definition
  9722.         // and attribute value) let's find range under caret first
  9723.         if (!curRange) {
  9724.             var matchedRanges = _.filter(ranges, function(r) {
  9725.                 return r.inside(selRange.end);
  9726.             });
  9727.             
  9728.             if (matchedRanges.length > 1)
  9729.                 return matchedRanges[1];
  9730.         }
  9731.         
  9732.         
  9733.         return _.find(ranges, function(r) {
  9734.             return r.end > selRange.end;
  9735.         });
  9736.     }
  9737.     
  9738.     /**
  9739.      * Search for opening tag in content, starting at specified position
  9740.      * @param {String} html Where to search tag
  9741.      * @param {Number} pos Character index where to start searching
  9742.      * @return {Range} Returns range if valid opening tag was found,
  9743.      * <code>null</code> otherwise
  9744.      */
  9745.     function findOpeningTagFromPosition(html, pos) {
  9746.         var tag;
  9747.         while (pos >= 0) {
  9748.             if (tag = getOpeningTagFromPosition(html, pos))
  9749.                 return tag;
  9750.             pos--;
  9751.         }
  9752.         
  9753.         return null;
  9754.     }
  9755.     
  9756.     /**
  9757.      * @param {String} html Where to search tag
  9758.      * @param {Number} pos Character index where to start searching
  9759.      * @return {Range} Returns range if valid opening tag was found,
  9760.      * <code>null</code> otherwise
  9761.      */
  9762.     function getOpeningTagFromPosition(html, pos) {
  9763.         var m;
  9764.         if (html.charAt(pos) == '<' && (m = html.substring(pos, html.length).match(startTag))) {
  9765.             return require('range').create(pos, m[0]);
  9766.         }
  9767.     }
  9768.     
  9769.     function isQuote(ch) {
  9770.         return ch == '"' || ch == "'";
  9771.     }
  9772.     
  9773.     /**
  9774.      * Makes all possible selection ranges for specified CSS property
  9775.      * @param {CSSProperty} property
  9776.      * @returns {Array}
  9777.      */
  9778.     function makePossibleRangesCSS(property) {
  9779.         // find all possible ranges, sorted by position and size
  9780.         var valueRange = property.valueRange(true);
  9781.         var result = [property.range(true), valueRange];
  9782.         var stringStream = require('stringStream');
  9783.         var cssEditTree = require('cssEditTree');
  9784.         var range = require('range');
  9785.         
  9786.         // locate parts of complex values.
  9787.         // some examples:
  9788.         // ΓÇô 1px solid red: 3 parts
  9789.         // ΓÇô arial, sans-serif: enumeration, 2 parts
  9790.         // ΓÇô url(image.png): function value part
  9791.         var value = property.value();
  9792.         _.each(property.valueParts(), function(r) {
  9793.             // add absolute range
  9794.             var clone = r.clone();
  9795.             result.push(clone.shift(valueRange.start));
  9796.             
  9797.             /** @type StringStream */
  9798.             var stream = stringStream.create(r.substring(value));
  9799.             if (stream.match(/^[\w\-]+\(/, true)) {
  9800.                 // we have a function, find values in it.
  9801.                 // but first add function contents
  9802.                 stream.start = stream.pos;
  9803.                 stream.skipToPair('(', ')');
  9804.                 var fnBody = stream.current();
  9805.                 result.push(range.create(clone.start + stream.start, fnBody));
  9806.                 
  9807.                 // find parts
  9808.                 _.each(cssEditTree.findParts(fnBody), function(part) {
  9809.                     result.push(range.create(clone.start + stream.start + part.start, part.substring(fnBody)));
  9810.                 });
  9811.             }
  9812.         });
  9813.         
  9814.         // optimize result: remove empty ranges and duplicates
  9815.         return _.chain(result)
  9816.             .filter(function(item) {
  9817.                 return !!item.length();
  9818.             })
  9819.             .uniq(false, function(item) {
  9820.                 return item.toString();
  9821.             })
  9822.             .value();
  9823.     }
  9824.     
  9825.     /**
  9826.      * Tries to find matched CSS property and nearest range for selection
  9827.      * @param {CSSRule} rule
  9828.      * @param {Range} selRange
  9829.      * @param {Boolean} isBackward
  9830.      * @returns {Range}
  9831.      */
  9832.     function matchedRangeForCSSProperty(rule, selRange, isBackward) {
  9833.         /** @type CSSProperty */
  9834.         var property = null;
  9835.         var possibleRanges, curRange = null, ix;
  9836.         var list = rule.list();
  9837.         var searchFn, nearestItemFn;
  9838.         
  9839.         if (isBackward) {
  9840.             list.reverse();
  9841.             searchFn = function(p) {
  9842.                 return p.range(true).start <= selRange.start;
  9843.             };
  9844.             nearestItemFn = function(r) {
  9845.                 return r.start < selRange.start;
  9846.             };
  9847.         } else {
  9848.             searchFn = function(p) {
  9849.                 return p.range(true).end >= selRange.end;
  9850.             };
  9851.             nearestItemFn = function(r) {
  9852.                 return r.end > selRange.start;
  9853.             };
  9854.         }
  9855.         
  9856.         // search for nearest to selection CSS property
  9857.         while (property = _.find(list, searchFn)) {
  9858.             possibleRanges = makePossibleRangesCSS(property);
  9859.             if (isBackward)
  9860.                 possibleRanges.reverse();
  9861.             
  9862.             // check if any possible range is already selected
  9863.             curRange = _.find(possibleRanges, function(r) {
  9864.                 return r.equal(selRange);
  9865.             });
  9866.             
  9867.             if (!curRange) {
  9868.                 // no selection, select nearest item
  9869.                 var matchedRanges = _.filter(possibleRanges, function(r) {
  9870.                     return r.inside(selRange.end);
  9871.                 });
  9872.                 
  9873.                 if (matchedRanges.length > 1) {
  9874.                     curRange = matchedRanges[1];
  9875.                     break;
  9876.                 }
  9877.                 
  9878.                 if (curRange = _.find(possibleRanges, nearestItemFn))
  9879.                     break;
  9880.             } else {
  9881.                 ix = _.indexOf(possibleRanges, curRange);
  9882.                 if (ix != possibleRanges.length - 1) {
  9883.                     curRange = possibleRanges[ix + 1];
  9884.                     break;
  9885.                 }
  9886.             }
  9887.             
  9888.             curRange = null;
  9889.             selRange.start = selRange.end = isBackward 
  9890.                 ? property.range(true).start - 1
  9891.                 : property.range(true).end + 1;
  9892.         }
  9893.         
  9894.         return curRange;
  9895.     }
  9896.     
  9897.     function findNextCSSItem(editor) {
  9898.         return findItem(editor, false, require('cssEditTree').extractRule, getRangeForNextItemInCSS);
  9899.     }
  9900.     
  9901.     function findPrevCSSItem(editor) {
  9902.         return findItem(editor, true, require('cssEditTree').extractRule, getRangeForPrevItemInCSS);
  9903.     }
  9904.     
  9905.     /**
  9906.      * Returns range for item to be selected in CSS after current caret 
  9907.      * (selection) position
  9908.      * @param {String} rule CSS rule declaration
  9909.      * @param {Number} offset Rule's position index inside content
  9910.      * @param {Range} selRange Selection range
  9911.      * @return {Range} Returns range if next item was found, <code>null</code> otherwise
  9912.      */
  9913.     function getRangeForNextItemInCSS(rule, offset, selRange) {
  9914.         var tree = require('cssEditTree').parse(rule, {
  9915.             offset: offset
  9916.         });
  9917.         
  9918.         // check if selector is matched
  9919.         var range = tree.nameRange(true);
  9920.         if (selRange.end < range.end) {
  9921.             return range;
  9922.         }
  9923.         
  9924.         return matchedRangeForCSSProperty(tree, selRange, false);
  9925.     }
  9926.     
  9927.     /**
  9928.      * Returns range for item to be selected in CSS before current caret 
  9929.      * (selection) position
  9930.      * @param {String} rule CSS rule declaration
  9931.      * @param {Number} offset Rule's position index inside content
  9932.      * @param {Range} selRange Selection range
  9933.      * @return {Range} Returns range if previous item was found, <code>null</code> otherwise
  9934.      */
  9935.     function getRangeForPrevItemInCSS(rule, offset, selRange) {
  9936.         var tree = require('cssEditTree').parse(rule, {
  9937.             offset: offset
  9938.         });
  9939.         
  9940.         var curRange = matchedRangeForCSSProperty(tree, selRange, true);
  9941.         
  9942.         if (!curRange) {
  9943.             // no matched property, try to match selector
  9944.             var range = tree.nameRange(true);
  9945.             if (selRange.start > range.start) {
  9946.                 return range;
  9947.             }
  9948.         }
  9949.         
  9950.         return curRange;
  9951.     }
  9952.     
  9953.     // XXX register actions
  9954.     var actions = require('actions');
  9955.     actions.add('select_next_item', function(editor){
  9956.         if (editor.getSyntax() == 'css')
  9957.             return findNextCSSItem(editor);
  9958.         else
  9959.             return findNextHTMLItem(editor);
  9960.     });
  9961.     
  9962.     actions.add('select_previous_item', function(editor){
  9963.         if (editor.getSyntax() == 'css')
  9964.             return findPrevCSSItem(editor);
  9965.         else
  9966.             return findPrevHTMLItem(editor);
  9967.     });
  9968. });/**
  9969.  * HTML pair matching (balancing) actions
  9970.  * @constructor
  9971.  * @memberOf __matchPairActionDefine
  9972.  * @param {Function} require
  9973.  * @param {Underscore} _
  9974.  */
  9975. emmet.exec(function(require, _) {
  9976.     /** @type emmet.actions */
  9977.     var actions = require('actions');
  9978.     var matcher = require('htmlMatcher');
  9979.     var lastMatch = null;
  9980.     
  9981.     /**
  9982.      * Find and select HTML tag pair
  9983.      * @param {IEmmetEditor} editor Editor instance
  9984.      * @param {String} direction Direction of pair matching: 'in' or 'out'. 
  9985.      * Default is 'out'
  9986.      */
  9987.     function matchPair(editor, direction) {
  9988.         direction = String((direction || 'out').toLowerCase());
  9989.         var info = require('editorUtils').outputInfo(editor);
  9990.         
  9991.         var range = require('range');
  9992.         /** @type Range */
  9993.         var sel = range.create(editor.getSelectionRange());
  9994.         var content = info.content;
  9995.         
  9996.         // validate previous match
  9997.         if (lastMatch && !lastMatch.range.equal(sel)) {
  9998.             lastMatch = null;
  9999.         }
  10000.         
  10001.         if (lastMatch && sel.length()) {
  10002.             if (direction == 'in') {
  10003.                 // user has previously selected tag and wants to move inward
  10004.                 if (lastMatch.type == 'tag' && !lastMatch.close) {
  10005.                     // unary tag was selected, can't move inward
  10006.                     return false;
  10007.                 } else {
  10008.                     if (lastMatch.range.equal(lastMatch.outerRange)) {
  10009.                         lastMatch.range = lastMatch.innerRange;
  10010.                     } else {
  10011.                         var narrowed = require('utils').narrowToNonSpace(content, lastMatch.innerRange);
  10012.                         lastMatch = matcher.find(content, narrowed.start + 1);
  10013.                         if (lastMatch && lastMatch.range.equal(sel) && lastMatch.outerRange.equal(sel)) {
  10014.                             lastMatch.range = lastMatch.innerRange;
  10015.                         }
  10016.                     }
  10017.                 }
  10018.             } else {
  10019.                 if (
  10020.                         !lastMatch.innerRange.equal(lastMatch.outerRange) 
  10021.                         && lastMatch.range.equal(lastMatch.innerRange) 
  10022.                         && sel.equal(lastMatch.range)) {
  10023.                     lastMatch.range = lastMatch.outerRange;
  10024.                 } else {
  10025.                     lastMatch = matcher.find(content, sel.start);
  10026.                     if (lastMatch && lastMatch.range.equal(sel) && lastMatch.innerRange.equal(sel)) {
  10027.                         lastMatch.range = lastMatch.outerRange;
  10028.                     }
  10029.                 }
  10030.             }
  10031.         } else {
  10032.             lastMatch = matcher.find(content, sel.start);
  10033.         }
  10034.         
  10035.         if (lastMatch && !lastMatch.range.equal(sel)) {
  10036.             editor.createSelection(lastMatch.range.start, lastMatch.range.end);
  10037.             return true;
  10038.         }
  10039.         
  10040.         lastMatch = null;
  10041.         return false;
  10042.     }
  10043.     
  10044.     actions.add('match_pair', matchPair, {hidden: true});
  10045.     actions.add('match_pair_inward', function(editor){
  10046.         return matchPair(editor, 'in');
  10047.     }, {label: 'HTML/Match Pair Tag (inward)'});
  10048.  
  10049.     actions.add('match_pair_outward', function(editor){
  10050.         return matchPair(editor, 'out');
  10051.     }, {label: 'HTML/Match Pair Tag (outward)'});
  10052.     
  10053.     /**
  10054.      * Moves caret to matching opening or closing tag
  10055.      * @param {IEmmetEditor} editor
  10056.      */
  10057.     actions.add('matching_pair', function(editor) {
  10058.         var content = String(editor.getContent());
  10059.         var caretPos = editor.getCaretPos();
  10060.         
  10061.         if (content.charAt(caretPos) == '<') 
  10062.             // looks like caret is outside of tag pair  
  10063.             caretPos++;
  10064.             
  10065.         var tag = matcher.tag(content, caretPos);
  10066.         if (tag && tag.close) { // exclude unary tags
  10067.             if (tag.open.range.inside(caretPos)) {
  10068.                 editor.setCaretPos(tag.close.range.start);
  10069.             } else {
  10070.                 editor.setCaretPos(tag.open.range.start);
  10071.             }
  10072.             
  10073.             return true;
  10074.         }
  10075.         
  10076.         return false;
  10077.     }, {label: 'HTML/Go To Matching Tag Pair'});
  10078. });/**
  10079.  * Gracefully removes tag under cursor
  10080.  * 
  10081.  * @param {Function} require
  10082.  * @param {Underscore} _ 
  10083.  */
  10084. emmet.exec(function(require, _) {
  10085.     require('actions').add('remove_tag', function(editor) {
  10086.         var utils = require('utils');
  10087.         var info = require('editorUtils').outputInfo(editor);
  10088.         
  10089.         // search for tag
  10090.         var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
  10091.         if (tag) {
  10092.             if (!tag.close) {
  10093.                 // simply remove unary tag
  10094.                 editor.replaceContent(utils.getCaretPlaceholder(), tag.range.start, tag.range.end);
  10095.             } else {
  10096.                 // remove tag and its newlines
  10097.                 /** @type Range */
  10098.                 var tagContentRange = utils.narrowToNonSpace(info.content, tag.innerRange);
  10099.                 /** @type Range */
  10100.                 var startLineBounds = utils.findNewlineBounds(info.content, tagContentRange.start);
  10101.                 var startLinePad = utils.getLinePadding(startLineBounds.substring(info.content));
  10102.                 var tagContent = tagContentRange.substring(info.content);
  10103.                 
  10104.                 tagContent = utils.unindentString(tagContent, startLinePad);
  10105.                 editor.replaceContent(utils.getCaretPlaceholder() + utils.escapeText(tagContent), tag.outerRange.start, tag.outerRange.end);
  10106.             }
  10107.             
  10108.             return true;
  10109.         }
  10110.         
  10111.         return false;
  10112.     }, {label: 'HTML/Remove Tag'});
  10113. });
  10114. /**
  10115.  * Splits or joins tag, e.g. transforms it into a short notation and vice versa:<br>
  10116.  * <div></div> ΓåÆ <div /> : join<br>
  10117.  * <div /> ΓåÆ <div></div> : split
  10118.  * @param {Function} require
  10119.  * @param {Underscore} _
  10120.  * @memberOf __splitJoinTagAction
  10121.  * @constructor
  10122.  */
  10123. emmet.exec(function(require, _) {
  10124.     /**
  10125.      * @param {IEmmetEditor} editor
  10126.      * @param {Object} profile
  10127.      * @param {Object} tag
  10128.      */
  10129.     function joinTag(editor, profile, tag) {
  10130.         /** @type emmet.utils */
  10131.         var utils = require('utils');
  10132.         
  10133.         // empty closing slash is a nonsense for this action
  10134.         var slash = profile.selfClosing() || ' /';
  10135.         var content = tag.open.range.substring(tag.source).replace(/\s*>$/, slash + '>');
  10136.         
  10137.         var caretPos = editor.getCaretPos();
  10138.         
  10139.         // update caret position
  10140.         if (content.length + tag.outerRange.start < caretPos) {
  10141.             caretPos = content.length + tag.outerRange.start;
  10142.         }
  10143.         
  10144.         content = utils.escapeText(content);
  10145.         editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
  10146.         editor.setCaretPos(caretPos);
  10147.         return true;
  10148.     }
  10149.     
  10150.     function splitTag(editor, profile, tag) {
  10151.         /** @type emmet.utils */
  10152.         var utils = require('utils');
  10153.         
  10154.         var nl = utils.getNewline();
  10155.         var pad = require('resources').getVariable('indentation');
  10156.         var caretPos = editor.getCaretPos();
  10157.         
  10158.         // define tag content depending on profile
  10159.         var tagContent = (profile.tag_nl === true) ? nl + pad + nl : '';
  10160.         var content = tag.outerContent().replace(/\s*\/>$/, '>');
  10161.         caretPos = tag.outerRange.start + content.length;
  10162.         content += tagContent + '</' + tag.open.name + '>';
  10163.         
  10164.         content = utils.escapeText(content);
  10165.         editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
  10166.         editor.setCaretPos(caretPos);
  10167.         return true;
  10168.     }
  10169.     
  10170.     require('actions').add('split_join_tag', function(editor, profileName) {
  10171.         var matcher = require('htmlMatcher');
  10172.         
  10173.         var info = require('editorUtils').outputInfo(editor, null, profileName);
  10174.         var profile = require('profile').get(info.profile);
  10175.         
  10176.         // find tag at current position
  10177.         var tag = matcher.tag(info.content, editor.getCaretPos());
  10178.         if (tag) {
  10179.             return tag.close 
  10180.                 ? joinTag(editor, profile, tag) 
  10181.                 : splitTag(editor, profile, tag);
  10182.         }
  10183.         
  10184.         return false;
  10185.     }, {label: 'HTML/Split\\Join Tag Declaration'});
  10186. });/**
  10187.  * Reflect CSS value: takes rule's value under caret and pastes it for the same 
  10188.  * rules with vendor prefixes
  10189.  * @constructor
  10190.  * @memberOf __reflectCSSActionDefine
  10191.  * @param {Function} require
  10192.  * @param {Underscore} _
  10193.  */
  10194. emmet.define('reflectCSSValue', function(require, _) {
  10195.     /**
  10196.      * @type HandlerList List of registered handlers
  10197.      */
  10198.     var handlers = require('handlerList').create();
  10199.     
  10200.     require('actions').add('reflect_css_value', function(editor) {
  10201.         if (editor.getSyntax() != 'css') return false;
  10202.         
  10203.         return require('actionUtils').compoundUpdate(editor, doCSSReflection(editor));
  10204.     }, {label: 'CSS/Reflect Value'});
  10205.     
  10206.     function doCSSReflection(editor) {
  10207.         /** @type emmet.cssEditTree */
  10208.         var cssEditTree = require('cssEditTree');
  10209.         var outputInfo = require('editorUtils').outputInfo(editor);
  10210.         var caretPos = editor.getCaretPos();
  10211.         
  10212.         var cssRule = cssEditTree.parseFromPosition(outputInfo.content, caretPos);
  10213.         if (!cssRule) return;
  10214.         
  10215.         var property = cssRule.itemFromPosition(caretPos, true);
  10216.         // no property under cursor, nothing to reflect
  10217.         if (!property) return;
  10218.         
  10219.         var oldRule = cssRule.source;
  10220.         var offset = cssRule.options.offset;
  10221.         var caretDelta = caretPos - offset - property.range().start;
  10222.         
  10223.         handlers.exec(false, [property]);
  10224.         
  10225.         if (oldRule !== cssRule.source) {
  10226.             return {
  10227.                 data:  cssRule.source,
  10228.                 start: offset,
  10229.                 end:   offset + oldRule.length,
  10230.                 caret: offset + property.range().start + caretDelta
  10231.             };
  10232.         }
  10233.     }
  10234.     
  10235.     /**
  10236.      * Returns regexp that should match reflected CSS property names
  10237.      * @param {String} name Current CSS property name
  10238.      * @return {RegExp}
  10239.      */
  10240.     function getReflectedCSSName(name) {
  10241.         name = require('cssEditTree').baseName(name);
  10242.         var vendorPrefix = '^(?:\\-\\w+\\-)?', m;
  10243.         
  10244.         if (name == 'opacity' || name == 'filter') {
  10245.             return new RegExp(vendorPrefix + '(?:opacity|filter)$');
  10246.         } else if (m = name.match(/^border-radius-(top|bottom)(left|right)/)) {
  10247.             // Mozilla-style border radius
  10248.             return new RegExp(vendorPrefix + '(?:' + name + '|border-' + m[1] + '-' + m[2] + '-radius)$');
  10249.         } else if (m = name.match(/^border-(top|bottom)-(left|right)-radius/)) { 
  10250.             return new RegExp(vendorPrefix + '(?:' + name + '|border-radius-' + m[1] + m[2] + ')$');
  10251.         }
  10252.         
  10253.         return new RegExp(vendorPrefix + name + '$');
  10254.     }
  10255.     
  10256.     /**
  10257.      * Reflects value from <code>donor</code> into <code>receiver</code>
  10258.      * @param {CSSProperty} donor Donor CSS property from which value should
  10259.      * be reflected
  10260.      * @param {CSSProperty} receiver Property that should receive reflected 
  10261.      * value from donor
  10262.      */
  10263.     function reflectValue(donor, receiver) {
  10264.         var value = getReflectedValue(donor.name(), donor.value(), 
  10265.                 receiver.name(), receiver.value());
  10266.         
  10267.         receiver.value(value);
  10268.     }
  10269.     
  10270.     /**
  10271.      * Returns value that should be reflected for <code>refName</code> CSS property
  10272.      * from <code>curName</code> property. This function is used for special cases,
  10273.      * when the same result must be achieved with different properties for different
  10274.      * browsers. For example: op╨░city:0.5; ΓåÆ filter:alpha(opacity=50);<br><br>
  10275.      * 
  10276.      * This function does value conversion between different CSS properties
  10277.      * 
  10278.      * @param {String} curName Current CSS property name
  10279.      * @param {String} curValue Current CSS property value
  10280.      * @param {String} refName Receiver CSS property's name 
  10281.      * @param {String} refValue Receiver CSS property's value
  10282.      * @return {String} New value for receiver property
  10283.      */
  10284.     function getReflectedValue(curName, curValue, refName, refValue) {
  10285.         var cssEditTree = require('cssEditTree');
  10286.         var utils = require('utils');
  10287.         curName = cssEditTree.baseName(curName);
  10288.         refName = cssEditTree.baseName(refName);
  10289.         
  10290.         if (curName == 'opacity' && refName == 'filter') {
  10291.             return refValue.replace(/opacity=[^)]*/i, 'opacity=' + Math.floor(parseFloat(curValue) * 100));
  10292.         } else if (curName == 'filter' && refName == 'opacity') {
  10293.             var m = curValue.match(/opacity=([^)]*)/i);
  10294.             return m ? utils.prettifyNumber(parseInt(m[1]) / 100) : refValue;
  10295.         }
  10296.         
  10297.         return curValue;
  10298.     }
  10299.     
  10300.     // XXX add default handler
  10301.     handlers.add(function(property) {
  10302.         var reName = getReflectedCSSName(property.name());
  10303.         _.each(property.parent.list(), function(p) {
  10304.             if (reName.test(p.name())) {
  10305.                 reflectValue(property, p);
  10306.             }
  10307.         });
  10308.     }, {order: -1});
  10309.     
  10310.     return {
  10311.         /**
  10312.          * Adds custom reflect handler. The passed function will receive matched
  10313.          * CSS property (as <code>CSSEditElement</code> object) and should
  10314.          * return <code>true</code> if it was performed successfully (handled 
  10315.          * reflection), <code>false</code> otherwise.
  10316.          * @param {Function} fn
  10317.          * @param {Object} options
  10318.          */
  10319.         addHandler: function(fn, options) {
  10320.             handlers.add(fn, options);
  10321.         },
  10322.         
  10323.         /**
  10324.          * Removes registered handler
  10325.          * @returns
  10326.          */
  10327.         removeHandler: function(fn) {
  10328.             handlers.remove(fn, options);
  10329.         }
  10330.     };
  10331. });/**
  10332.  * Evaluates simple math expression under caret
  10333.  * @param {Function} require
  10334.  * @param {Underscore} _ 
  10335.  */
  10336. emmet.exec(function(require, _) {
  10337.     require('actions').add('evaluate_math_expression', function(editor) {
  10338.         var actionUtils = require('actionUtils');
  10339.         var utils = require('utils');
  10340.         
  10341.         var content = String(editor.getContent());
  10342.         var chars = '.+-*/\\';
  10343.         
  10344.         /** @type Range */
  10345.         var sel = require('range').create(editor.getSelectionRange());
  10346.         if (!sel.length()) {
  10347.             sel = actionUtils.findExpressionBounds(editor, function(ch) {
  10348.                 return utils.isNumeric(ch) || chars.indexOf(ch) != -1;
  10349.             });
  10350.         }
  10351.         
  10352.         if (sel && sel.length()) {
  10353.             var expr = sel.substring(content);
  10354.             
  10355.             // replace integral division: 11\2 => Math.round(11/2) 
  10356.             expr = expr.replace(/([\d\.\-]+)\\([\d\.\-]+)/g, 'Math.round($1/$2)');
  10357.             
  10358.             try {
  10359.                 var result = utils.prettifyNumber(new Function('return ' + expr)());
  10360.                 editor.replaceContent(result, sel.start, sel.end);
  10361.                 editor.setCaretPos(sel.start + result.length);
  10362.                 return true;
  10363.             } catch (e) {}
  10364.         }
  10365.         
  10366.         return false;
  10367.     }, {label: 'Numbers/Evaluate Math Expression'});
  10368. });
  10369. /**
  10370.  * Increment/decrement number under cursor
  10371.  * @param {Function} require
  10372.  * @param {Underscore} _
  10373.  */
  10374. emmet.exec(function(require, _) {
  10375.     /**
  10376.      * Extract number from current caret position of the <code>editor</code> and
  10377.      * increment it by <code>step</code>
  10378.      * @param {IEmmetEditor} editor
  10379.      * @param {Number} step Increment step (may be negative)
  10380.      */
  10381.     function incrementNumber(editor, step) {
  10382.         var utils = require('utils');
  10383.         var actionUtils = require('actionUtils');
  10384.         
  10385.         var hasSign = false;
  10386.         var hasDecimal = false;
  10387.             
  10388.         var r = actionUtils.findExpressionBounds(editor, function(ch, pos, content) {
  10389.             if (utils.isNumeric(ch))
  10390.                 return true;
  10391.             if (ch == '.') {
  10392.                 // make sure that next character is numeric too
  10393.                 if (!utils.isNumeric(content.charAt(pos + 1)))
  10394.                     return false;
  10395.                 
  10396.                 return hasDecimal ? false : hasDecimal = true;
  10397.             }
  10398.             if (ch == '-')
  10399.                 return hasSign ? false : hasSign = true;
  10400.                 
  10401.             return false;
  10402.         });
  10403.             
  10404.         if (r && r.length()) {
  10405.             var strNum = r.substring(String(editor.getContent()));
  10406.             var num = parseFloat(strNum);
  10407.             if (!_.isNaN(num)) {
  10408.                 num = utils.prettifyNumber(num + step);
  10409.                 
  10410.                 // do we have zero-padded number?
  10411.                 if (/^(\-?)0+[1-9]/.test(strNum)) {
  10412.                     var minus = '';
  10413.                     if (RegExp.$1) {
  10414.                         minus = '-';
  10415.                         num = num.substring(1);
  10416.                     }
  10417.                         
  10418.                     var parts = num.split('.');
  10419.                     parts[0] = utils.zeroPadString(parts[0], intLength(strNum));
  10420.                     num = minus + parts.join('.');
  10421.                 }
  10422.                 
  10423.                 editor.replaceContent(num, r.start, r.end);
  10424.                 editor.createSelection(r.start, r.start + num.length);
  10425.                 return true;
  10426.             }
  10427.         }
  10428.         
  10429.         return false;
  10430.     }
  10431.     
  10432.     /**
  10433.      * Returns length of integer part of number
  10434.      * @param {String} num
  10435.      */
  10436.     function intLength(num) {
  10437.         num = num.replace(/^\-/, '');
  10438.         if (~num.indexOf('.')) {
  10439.             return num.split('.')[0].length;
  10440.         }
  10441.         
  10442.         return num.length;
  10443.     }
  10444.     
  10445.     var actions = require('actions');
  10446.     _.each([1, -1, 10, -10, 0.1, -0.1], function(num) {
  10447.         var prefix = num > 0 ? 'increment' : 'decrement';
  10448.         
  10449.         actions.add(prefix + '_number_by_' + String(Math.abs(num)).replace('.', '').substring(0, 2), function(editor) {
  10450.             return incrementNumber(editor, num);
  10451.         }, {label: 'Numbers/' + prefix.charAt(0).toUpperCase() + prefix.substring(1) + ' number by ' + Math.abs(num)});
  10452.     });
  10453. });/**
  10454.  * Actions to insert line breaks. Some simple editors (like browser's 
  10455.  * <textarea>, for example) do not provide such simple things
  10456.  * @param {Function} require
  10457.  * @param {Underscore} _
  10458.  */
  10459. emmet.exec(function(require, _) {
  10460.     var actions = require('actions');
  10461.     /** @type emmet.preferences */
  10462.     var prefs = require('preferences');
  10463.     
  10464.     // setup default preferences
  10465.     prefs.define('css.closeBraceIndentation', '\n',
  10466.             'Indentation before closing brace of CSS rule. Some users prefere ' 
  10467.             + 'indented closing brace of CSS rule for better readability. '
  10468.             + 'This preferenceΓÇÖs value will be automatically inserted before '
  10469.             + 'closing brace when user adds newline in newly created CSS rule '
  10470.             + '(e.g. when ΓÇ£Insert formatted linebreakΓÇ¥ action will be performed ' 
  10471.             + 'in CSS file). If youΓÇÖre such user, you may want to write put a value ' 
  10472.             + 'like <code>\\n\\t</code> in this preference.');
  10473.     
  10474.     /**
  10475.      * Inserts newline character with proper indentation in specific positions only.
  10476.      * @param {IEmmetEditor} editor
  10477.      * @return {Boolean} Returns <code>true</code> if line break was inserted 
  10478.      */
  10479.     actions.add('insert_formatted_line_break_only', function(editor) {
  10480.         var utils = require('utils');
  10481.         /** @type emmet.resources */
  10482.         var res = require('resources');
  10483.         
  10484.         var info = require('editorUtils').outputInfo(editor);
  10485.         var caretPos = editor.getCaretPos();
  10486.         var nl = utils.getNewline();
  10487.         
  10488.         if (_.include(['html', 'xml', 'xsl'], info.syntax)) {
  10489.             var pad = res.getVariable('indentation');
  10490.             // let's see if we're breaking newly created tag
  10491.             var tag = require('htmlMatcher').tag(info.content, caretPos);
  10492.             if (tag && !tag.innerRange.length()) {
  10493.                 editor.replaceContent(nl + pad + utils.getCaretPlaceholder() + nl, caretPos);
  10494.                 return true;
  10495.             }
  10496.         } else if (info.syntax == 'css') {
  10497.             /** @type String */
  10498.             var content = info.content;
  10499.             if (caretPos && content.charAt(caretPos - 1) == '{') {
  10500.                 var append = prefs.get('css.closeBraceIndentation');
  10501.                 var pad = res.getVariable('indentation');
  10502.                 
  10503.                 var hasCloseBrace = content.charAt(caretPos) == '}';
  10504.                 if (!hasCloseBrace) {
  10505.                     // do we really need special formatting here?
  10506.                     // check if this is really a newly created rule,
  10507.                     // look ahead for a closing brace
  10508.                     for (var i = caretPos, il = content.length, ch; i < il; i++) {
  10509.                         ch = content.charAt(i);
  10510.                         if (ch == '{') {
  10511.                             // ok, this is a new rule without closing brace
  10512.                             break;
  10513.                         }
  10514.                         
  10515.                         if (ch == '}') {
  10516.                             // not a new rule, just add indentation
  10517.                             append = '';
  10518.                             hasCloseBrace = true;
  10519.                             break;
  10520.                         }
  10521.                     }
  10522.                 }
  10523.                 
  10524.                 if (!hasCloseBrace) {
  10525.                     append += '}';
  10526.                 }
  10527.                 
  10528.                 // defining rule set
  10529.                 var insValue = nl + pad + utils.getCaretPlaceholder() + append;
  10530.                 editor.replaceContent(insValue, caretPos);
  10531.                 return true;
  10532.             }
  10533.         }
  10534.             
  10535.         return false;
  10536.     }, {hidden: true});
  10537.     
  10538.     /**
  10539.      * Inserts newline character with proper indentation. This action is used in
  10540.      * editors that doesn't have indentation control (like textarea element) to 
  10541.      * provide proper indentation
  10542.      * @param {IEmmetEditor} editor Editor instance
  10543.      */
  10544.     actions.add('insert_formatted_line_break', function(editor) {
  10545.         if (!actions.run('insert_formatted_line_break_only', editor)) {
  10546.             var utils = require('utils');
  10547.             
  10548.             var curPadding = require('editorUtils').getCurrentLinePadding(editor);
  10549.             var content = String(editor.getContent());
  10550.             var caretPos = editor.getCaretPos();
  10551.             var len = content.length;
  10552.             var nl = utils.getNewline();
  10553.                 
  10554.             // check out next line padding
  10555.             var lineRange = editor.getCurrentLineRange();
  10556.             var nextPadding = '';
  10557.                 
  10558.             for (var i = lineRange.end + 1, ch; i < len; i++) {
  10559.                 ch = content.charAt(i);
  10560.                 if (ch == ' ' || ch == '\t')
  10561.                     nextPadding += ch;
  10562.                 else
  10563.                     break;
  10564.             }
  10565.             
  10566.             if (nextPadding.length > curPadding.length)
  10567.                 editor.replaceContent(nl + nextPadding, caretPos, caretPos, true);
  10568.             else
  10569.                 editor.replaceContent(nl, caretPos);
  10570.         }
  10571.         
  10572.         return true;
  10573.     }, {hidden: true});
  10574. });/**
  10575.  * Merges selected lines or lines between XHTML tag pairs
  10576.  * @param {Function} require
  10577.  * @param {Underscore} _
  10578.  */
  10579. emmet.exec(function(require, _) {
  10580.     require('actions').add('merge_lines', function(editor) {
  10581.         var matcher = require('htmlMatcher');
  10582.         var utils = require('utils');
  10583.         var editorUtils = require('editorUtils');
  10584.         var info = editorUtils.outputInfo(editor);
  10585.         
  10586.         /** @type Range */
  10587.         var selection = require('range').create(editor.getSelectionRange());
  10588.         if (!selection.length()) {
  10589.             // find matching tag
  10590.             var pair = matcher.find(info.content, editor.getCaretPos());
  10591.             if (pair) {
  10592.                 selection = pair.outerRange;
  10593.             }
  10594.         }
  10595.         
  10596.         if (selection.length()) {
  10597.             // got range, merge lines
  10598.             var text =  selection.substring(info.content);
  10599.             var lines = utils.splitByLines(text);
  10600.             
  10601.             for (var i = 1; i < lines.length; i++) {
  10602.                 lines[i] = lines[i].replace(/^\s+/, '');
  10603.             }
  10604.             
  10605.             text = lines.join('').replace(/\s{2,}/, ' ');
  10606.             var textLen = text.length;
  10607.             text = utils.escapeText(text);
  10608.             editor.replaceContent(text, selection.start, selection.end);
  10609.             editor.createSelection(selection.start, selection.start + textLen);
  10610.             
  10611.             return true;
  10612.         }
  10613.         
  10614.         return false;
  10615.     });
  10616. });/**
  10617.  * Encodes/decodes image under cursor to/from base64
  10618.  * @param {IEmmetEditor} editor
  10619.  * @since 0.65
  10620.  * 
  10621.  * @memberOf __base64ActionDefine
  10622.  * @constructor
  10623.  * @param {Function} require
  10624.  * @param {Underscore} _
  10625.  */
  10626. emmet.exec(function(require, _) {
  10627.     require('actions').add('encode_decode_data_url', function(editor) {
  10628.         var data = String(editor.getSelection());
  10629.         var caretPos = editor.getCaretPos();
  10630.             
  10631.         if (!data) {
  10632.             // no selection, try to find image bounds from current caret position
  10633.             var text = String(editor.getContent()),  m;
  10634.             while (caretPos-- >= 0) {
  10635.                 if (startsWith('src=', text, caretPos)) { // found <img src="">
  10636.                     if (m = text.substr(caretPos).match(/^(src=(["'])?)([^'"<>\s]+)\1?/)) {
  10637.                         data = m[3];
  10638.                         caretPos += m[1].length;
  10639.                     }
  10640.                     break;
  10641.                 } else if (startsWith('url(', text, caretPos)) { // found CSS url() pattern
  10642.                     if (m = text.substr(caretPos).match(/^(url\((['"])?)([^'"\)\s]+)\1?/)) {
  10643.                         data = m[3];
  10644.                         caretPos += m[1].length;
  10645.                     }
  10646.                     break;
  10647.                 }
  10648.             }
  10649.         }
  10650.         
  10651.         if (data) {
  10652.             if (startsWith('data:', data))
  10653.                 return decodeFromBase64(editor, data, caretPos);
  10654.             else
  10655.                 return encodeToBase64(editor, data, caretPos);
  10656.         }
  10657.         
  10658.         return false;
  10659.     }, {label: 'Encode\\Decode data:URL image'});
  10660.     
  10661.     /**
  10662.      * Test if <code>text</code> starts with <code>token</code> at <code>pos</code>
  10663.      * position. If <code>pos</code> is omitted, search from beginning of text 
  10664.      * @param {String} token Token to test
  10665.      * @param {String} text Where to search
  10666.      * @param {Number} pos Position where to start search
  10667.      * @return {Boolean}
  10668.      * @since 0.65
  10669.      */
  10670.     function startsWith(token, text, pos) {
  10671.         pos = pos || 0;
  10672.         return text.charAt(pos) == token.charAt(0) && text.substr(pos, token.length) == token;
  10673.     }
  10674.     
  10675.     /**
  10676.      * Encodes image to base64
  10677.      * 
  10678.      * @param {IEmmetEditor} editor
  10679.      * @param {String} imgPath Path to image
  10680.      * @param {Number} pos Caret position where image is located in the editor
  10681.      * @return {Boolean}
  10682.      */
  10683.     function encodeToBase64(editor, imgPath, pos) {
  10684.         var file = require('file');
  10685.         var actionUtils = require('actionUtils');
  10686.         
  10687.         var editorFile = editor.getFilePath();
  10688.         var defaultMimeType = 'application/octet-stream';
  10689.             
  10690.         if (editorFile === null) {
  10691.             throw "You should save your file before using this action";
  10692.         }
  10693.         
  10694.         // locate real image path
  10695.         var realImgPath = file.locateFile(editorFile, imgPath);
  10696.         if (realImgPath === null) {
  10697.             throw "Can't find " + imgPath + ' file';
  10698.         }
  10699.         
  10700.         var b64 = require('base64').encode(String(file.read(realImgPath)));
  10701.         if (!b64) {
  10702.             throw "Can't encode file content to base64";
  10703.         }
  10704.         
  10705.         b64 = 'data:' + (actionUtils.mimeTypes[String(file.getExt(realImgPath))] || defaultMimeType) +
  10706.             ';base64,' + b64;
  10707.             
  10708.         editor.replaceContent('$0' + b64, pos, pos + imgPath.length);
  10709.         return true;
  10710.     }
  10711.  
  10712.     /**
  10713.      * Decodes base64 string back to file.
  10714.      * @param {IEmmetEditor} editor
  10715.      * @param {String} data Base64-encoded file content
  10716.      * @param {Number} pos Caret position where image is located in the editor
  10717.      */
  10718.     function decodeFromBase64(editor, data, pos) {
  10719.         // ask user to enter path to file
  10720.         var filePath = String(editor.prompt('Enter path to file (absolute or relative)'));
  10721.         if (!filePath)
  10722.             return false;
  10723.             
  10724.         var file = require('file');
  10725.         var absPath = file.createPath(editor.getFilePath(), filePath);
  10726.         if (!absPath) {
  10727.             throw "Can't save file";
  10728.         }
  10729.         
  10730.         file.save(absPath, require('base64').decode( data.replace(/^data\:.+?;.+?,/, '') ));
  10731.         editor.replaceContent('$0' + filePath, pos, pos + data.length);
  10732.         return true;
  10733.     }
  10734. });
  10735. /**
  10736.  * Automatically updates image size attributes in HTML's <img> element or
  10737.  * CSS rule
  10738.  * @param {Function} require
  10739.  * @param {Underscore} _
  10740.  * @constructor
  10741.  * @memberOf __updateImageSizeAction
  10742.  */
  10743. emmet.exec(function(require, _) {
  10744.     /**
  10745.      * Updates image size of <img src=""> tag
  10746.      * @param {IEmmetEditor} editor
  10747.      */
  10748.     function updateImageSizeHTML(editor) {
  10749.         var offset = editor.getCaretPos();
  10750.         
  10751.         // find tag from current caret position
  10752.         var info = require('editorUtils').outputInfo(editor);
  10753.         var xmlElem = require('xmlEditTree').parseFromPosition(info.content, offset, true);
  10754.         if (xmlElem && (xmlElem.name() || '').toLowerCase() == 'img') {
  10755.             
  10756.             var size = getImageSizeForSource(editor, xmlElem.value('src'));
  10757.             if (size) {
  10758.                 var compoundData = xmlElem.range(true);
  10759.                 xmlElem.value('width', size.width);
  10760.                 xmlElem.value('height', size.height, xmlElem.indexOf('width') + 1);
  10761.                 
  10762.                 return _.extend(compoundData, {
  10763.                     data: xmlElem.toString(),
  10764.                     caret: offset
  10765.                 });
  10766.             }
  10767.         }
  10768.         
  10769.         return null;
  10770.     }
  10771.     
  10772.     /**
  10773.      * Updates image size of CSS property
  10774.      * @param {IEmmetEditor} editor
  10775.      */
  10776.     function updateImageSizeCSS(editor) {
  10777.         var offset = editor.getCaretPos();
  10778.         
  10779.         // find tag from current caret position
  10780.         var info = require('editorUtils').outputInfo(editor);
  10781.         var cssRule = require('cssEditTree').parseFromPosition(info.content, offset, true);
  10782.         if (cssRule) {
  10783.             // check if there is property with image under caret
  10784.             var prop = cssRule.itemFromPosition(offset, true), m;
  10785.             if (prop && (m = /url\((["']?)(.+?)\1\)/i.exec(prop.value() || ''))) {
  10786.                 var size = getImageSizeForSource(editor, m[2]);
  10787.                 if (size) {
  10788.                     var compoundData = cssRule.range(true);
  10789.                     cssRule.value('width', size.width + 'px');
  10790.                     cssRule.value('height', size.height + 'px', cssRule.indexOf('width') + 1);
  10791.                     
  10792.                     return _.extend(compoundData, {
  10793.                         data: cssRule.toString(),
  10794.                         caret: offset
  10795.                     });
  10796.                 }
  10797.             }
  10798.         }
  10799.         
  10800.         return null;
  10801.     }
  10802.     
  10803.     /**
  10804.      * Returns image dimensions for source
  10805.      * @param {IEmmetEditor} editor
  10806.      * @param {String} src Image source (path or data:url)
  10807.      */
  10808.     function getImageSizeForSource(editor, src) {
  10809.         var fileContent;
  10810.         if (src) {
  10811.             // check if it is data:url
  10812.             if (/^data:/.test(src)) {
  10813.                 fileContent = require('base64').decode( src.replace(/^data\:.+?;.+?,/, '') );
  10814.             } else {
  10815.                 var file = require('file');
  10816.                 var absPath = file.locateFile(editor.getFilePath(), src);
  10817.                 if (absPath === null) {
  10818.                     throw "Can't find " + src + ' file';
  10819.                 }
  10820.                 
  10821.                 fileContent = String(file.read(absPath));
  10822.             }
  10823.             
  10824.             return require('actionUtils').getImageSize(fileContent);
  10825.         }
  10826.     }
  10827.     
  10828.     require('actions').add('update_image_size', function(editor) {
  10829.         var result;
  10830.         // this action will definitely wonΓÇÖt work in SASS dialect,
  10831.         // but may work in SCSS or LESS
  10832.         if (_.include(['css', 'less', 'scss'], String(editor.getSyntax()))) {
  10833.             result = updateImageSizeCSS(editor);
  10834.         } else {
  10835.             result = updateImageSizeHTML(editor);
  10836.         }
  10837.         
  10838.         return require('actionUtils').compoundUpdate(editor, result);
  10839.     });
  10840. });/**
  10841.  * "Lorem ipsum" text generator. Matches <code>lipsum(num)?</code> or 
  10842.  * <code>lorem(num)?</code> abbreviation.
  10843.  * This code is based on Django's contribution: 
  10844.  * https://code.djangoproject.com/browser/django/trunk/django/contrib/webdesign/lorem_ipsum.py
  10845.  * <br><br>
  10846.  * Examples to test:<br>
  10847.  * <code>lipsum</code> ΓÇô generates 30 words text.<br>
  10848.  * <code>lipsum*6</code> ΓÇô generates 6 paragraphs (autowrapped with <p> element) of text.<br>
  10849.  * <code>ol>lipsum10*5</code> ΓÇö generates ordered list with 5 list items (autowrapped with <li> tag)
  10850.  * with text of 10 words on each line<br>
  10851.  * <code>span*3>lipsum20</code> ΓÇô generates 3 paragraphs of 20-words text, each wrapped with <span> element .
  10852.  * Each paragraph phrase is unique   
  10853.  * @param {Function} require
  10854.  * @param {Underscore} _ 
  10855.  * @constructor
  10856.  * @memberOf __loremIpsumGeneratorDefine
  10857.  */
  10858. emmet.exec(function(require, _) {
  10859.     /**
  10860.      * @param {AbbreviationNode} tree
  10861.      * @param {Object} options
  10862.      */
  10863.     require('abbreviationParser').addPreprocessor(function(tree, options) {
  10864.         var re = /^(?:lorem|lipsum)(\d*)$/i, match;
  10865.         
  10866.         /** @param {AbbreviationNode} node */
  10867.         tree.findAll(function(node) {
  10868.             if (node._name && (match = node._name.match(re))) {
  10869.                 var wordCound = match[1] || 30;
  10870.                 
  10871.                 // force node name resolving if node should be repeated
  10872.                 // or contains attributes. In this case, node should be outputed
  10873.                 // as tag, otherwise as text-only node
  10874.                 node._name = '';
  10875.                 node.data('forceNameResolving', node.isRepeating() || node.attributeList().length);
  10876.                 node.data('pasteOverwrites', true);
  10877.                 node.data('paste', function(i, content) {
  10878.                     return paragraph(wordCound, !i);
  10879.                 });
  10880.             }
  10881.         });
  10882.     });
  10883.     
  10884.     var COMMON_P = 'lorem ipsum dolor sit amet consectetur adipisicing elit'.split(' ');
  10885.     
  10886.     var WORDS = ['exercitationem', 'perferendis', 'perspiciatis', 'laborum', 'eveniet',
  10887.                  'sunt', 'iure', 'nam', 'nobis', 'eum', 'cum', 'officiis', 'excepturi',
  10888.                  'odio', 'consectetur', 'quasi', 'aut', 'quisquam', 'vel', 'eligendi',
  10889.                  'itaque', 'non', 'odit', 'tempore', 'quaerat', 'dignissimos',
  10890.                  'facilis', 'neque', 'nihil', 'expedita', 'vitae', 'vero', 'ipsum',
  10891.                  'nisi', 'animi', 'cumque', 'pariatur', 'velit', 'modi', 'natus',
  10892.                  'iusto', 'eaque', 'sequi', 'illo', 'sed', 'ex', 'et', 'voluptatibus',
  10893.                  'tempora', 'veritatis', 'ratione', 'assumenda', 'incidunt', 'nostrum',
  10894.                  'placeat', 'aliquid', 'fuga', 'provident', 'praesentium', 'rem',
  10895.                  'necessitatibus', 'suscipit', 'adipisci', 'quidem', 'possimus',
  10896.                  'voluptas', 'debitis', 'sint', 'accusantium', 'unde', 'sapiente',
  10897.                  'voluptate', 'qui', 'aspernatur', 'laudantium', 'soluta', 'amet',
  10898.                  'quo', 'aliquam', 'saepe', 'culpa', 'libero', 'ipsa', 'dicta',
  10899.                  'reiciendis', 'nesciunt', 'doloribus', 'autem', 'impedit', 'minima',
  10900.                  'maiores', 'repudiandae', 'ipsam', 'obcaecati', 'ullam', 'enim',
  10901.                  'totam', 'delectus', 'ducimus', 'quis', 'voluptates', 'dolores',
  10902.                  'molestiae', 'harum', 'dolorem', 'quia', 'voluptatem', 'molestias',
  10903.                  'magni', 'distinctio', 'omnis', 'illum', 'dolorum', 'voluptatum', 'ea',
  10904.                  'quas', 'quam', 'corporis', 'quae', 'blanditiis', 'atque', 'deserunt',
  10905.                  'laboriosam', 'earum', 'consequuntur', 'hic', 'cupiditate',
  10906.                  'quibusdam', 'accusamus', 'ut', 'rerum', 'error', 'minus', 'eius',
  10907.                  'ab', 'ad', 'nemo', 'fugit', 'officia', 'at', 'in', 'id', 'quos',
  10908.                  'reprehenderit', 'numquam', 'iste', 'fugiat', 'sit', 'inventore',
  10909.                  'beatae', 'repellendus', 'magnam', 'recusandae', 'quod', 'explicabo',
  10910.                  'doloremque', 'aperiam', 'consequatur', 'asperiores', 'commodi',
  10911.                  'optio', 'dolor', 'labore', 'temporibus', 'repellat', 'veniam',
  10912.                  'architecto', 'est', 'esse', 'mollitia', 'nulla', 'a', 'similique',
  10913.                  'eos', 'alias', 'dolore', 'tenetur', 'deleniti', 'porro', 'facere',
  10914.                  'maxime', 'corrupti'];
  10915.     
  10916.     /**
  10917.      * Returns random integer between <code>from</code> and <code>to</code> values
  10918.      * @param {Number} from
  10919.      * @param {Number} to
  10920.      * @returns {Number}
  10921.      */
  10922.     function randint(from, to) {
  10923.         return Math.round(Math.random() * (to - from) + from);
  10924.     }
  10925.     
  10926.     /**
  10927.      * @param {Array} arr
  10928.      * @param {Number} count
  10929.      * @returns {Array}
  10930.      */
  10931.     function sample(arr, count) {
  10932.         var len = arr.length;
  10933.         var iterations = Math.min(len, count);
  10934.         var result = [];
  10935.         while (result.length < iterations) {
  10936.             var randIx = randint(0, len - 1);
  10937.             if (!_.include(result, randIx))
  10938.                 result.push(randIx);
  10939.         }
  10940.         
  10941.         return _.map(result, function(ix) {
  10942.             return arr[ix];
  10943.         });
  10944.     }
  10945.     
  10946.     function choice(val) {
  10947.         if (_.isString(val))
  10948.             return val.charAt(randint(0, val.length - 1));
  10949.         
  10950.         return val[randint(0, val.length - 1)];
  10951.     }
  10952.     
  10953.     function sentence(words, end) {
  10954.         if (words.length) {
  10955.             words[0] = words[0].charAt(0).toUpperCase() + words[0].substring(1);
  10956.         }
  10957.         
  10958.         return words.join(' ') + (end || choice('?!...')); // more dots that question marks
  10959.     }
  10960.     
  10961.     /**
  10962.      * Insert commas at randomly selected words. This function modifies values
  10963.      * inside <code>words</code> array 
  10964.      * @param {Array} words
  10965.      */
  10966.     function insertCommas(words) {
  10967.         var len = words.length;
  10968.         var totalCommas = 0;
  10969.         
  10970.         if (len > 3 && len <= 6) {
  10971.             totalCommas = randint(0, 1);
  10972.         } else if (len > 6 && len <= 12) {
  10973.             totalCommas = randint(0, 2);
  10974.         } else {
  10975.             totalCommas = randint(1, 4);
  10976.         }
  10977.         
  10978.         _.each(sample(_.range(totalCommas)), function(ix) {
  10979.             words[ix] += ',';
  10980.         });
  10981.     }
  10982.     
  10983.     /**
  10984.      * Generate a paragraph of "Lorem ipsum" text
  10985.      * @param {Number} wordCount Words count in paragraph
  10986.      * @param {Boolean} startWithCommon Should paragraph start with common 
  10987.      * "lorem ipsum" sentence.
  10988.      * @returns {String}
  10989.      */
  10990.     function paragraph(wordCount, startWithCommon) {
  10991.         var result = [];
  10992.         var totalWords = 0;
  10993.         var words;
  10994.         
  10995.         wordCount = parseInt(wordCount, 10);
  10996.         
  10997.         if (startWithCommon) {
  10998.             words = COMMON_P.slice(0, wordCount);
  10999.             if (words.length > 5)
  11000.                 words[4] += ',';
  11001.             totalWords += words.length;
  11002.             result.push(sentence(words, '.'));
  11003.         }
  11004.         
  11005.         while (totalWords < wordCount) {
  11006.             words = sample(WORDS, Math.min(randint(3, 12) * randint(1, 5), wordCount - totalWords));
  11007.             totalWords += words.length;
  11008.             insertCommas(words);
  11009.             result.push(sentence(words));
  11010.         }
  11011.         
  11012.         return result.join(' ');
  11013.     }
  11014. });/**
  11015.  * Resolver for fast CSS typing. Handles abbreviations with the following 
  11016.  * notation:<br>
  11017.  * 
  11018.  * <code>(-vendor prefix)?property(value)*(!)?</code>
  11019.  * 
  11020.  * <br><br>
  11021.  * <b>Abbreviation handling</b><br>
  11022.  * 
  11023.  * By default, Emmet searches for matching snippet definition for provided abbreviation.
  11024.  * If snippet wasn't found, Emmet automatically generates element with 
  11025.  * abbreviation's name. For example, <code>foo</code> abbreviation will generate
  11026.  * <code><foo></foo></code> output.
  11027.  * <br><br>
  11028.  * This module will capture all expanded properties and upgrade them with values, 
  11029.  * vendor prefixes and !important declarations. All unmatched abbreviations will 
  11030.  * be automatically transformed into <code>property-name: ${1}</code> snippets. 
  11031.  * 
  11032.  * <b>Vendor prefixes<b><br>
  11033.  * 
  11034.  * If CSS-property is preceded with dash, resolver should output property with
  11035.  * all <i>known</i> vendor prefixes. For example, if <code>brad</code> 
  11036.  * abbreviation generates <code>border-radius: ${value};</code> snippet,
  11037.  * the <code>-brad</code> abbreviation should generate:
  11038.  * <pre><code>
  11039.  * -webkit-border-radius: ${value};
  11040.  * -moz-border-radius: ${value};
  11041.  * border-radius: ${value};
  11042.  * </code></pre>
  11043.  * Note that <i>o</i> and <i>ms</i> prefixes are omitted since Opera and IE 
  11044.  * supports unprefixed property.<br><br>
  11045.  * 
  11046.  * Users can also provide an explicit list of one-character prefixes for any
  11047.  * CSS property. For example, <code>-wm-float</code> will produce
  11048.  * 
  11049.  * <pre><code>
  11050.  * -webkit-float: ${1};
  11051.  * -moz-float: ${1};
  11052.  * float: ${1};
  11053.  * </code></pre>
  11054.  * 
  11055.  * Although this example looks pointless, users can use this feature to write
  11056.  * cutting-edge properties implemented by browser vendors recently.  
  11057.  * 
  11058.  * @constructor
  11059.  * @memberOf __cssResolverDefine
  11060.  * @param {Function} require
  11061.  * @param {Underscore} _
  11062.  */
  11063. emmet.define('cssResolver', function(require, _) {
  11064.     /** Back-reference to module */
  11065.     var module = null;
  11066.     
  11067.     var prefixObj = {
  11068.         /** Real vendor prefix name */
  11069.         prefix: 'emmet',
  11070.         
  11071.         /** 
  11072.          * Indicates this prefix is obsolete and should't be used when user 
  11073.          * wants to generate all-prefixed properties
  11074.          */
  11075.         obsolete: false,
  11076.         
  11077.         /**
  11078.          * Returns prefixed CSS property name
  11079.          * @param {String} name Unprefixed CSS property
  11080.          */
  11081.         transformName: function(name) {
  11082.             return '-' + this.prefix + '-' + name;
  11083.         },
  11084.         
  11085.         /**
  11086.          * List of unprefixed CSS properties that supported by 
  11087.          * current prefix. This list is used to generate all-prefixed property
  11088.          * @returns {Array} 
  11089.          */
  11090.         properties: function() {
  11091.             return getProperties('css.' + this.prefix + 'Properties') || [];
  11092.         },
  11093.         
  11094.         /**
  11095.          * Check if given property is supported by current prefix
  11096.          * @param name
  11097.          */
  11098.         supports: function(name) {
  11099.             return _.include(this.properties(), name);
  11100.         }
  11101.     };
  11102.     
  11103.     
  11104.     /** 
  11105.      * List of registered one-character prefixes. Key is a one-character prefix, 
  11106.      * value is an <code>prefixObj</code> object
  11107.      */
  11108.     var vendorPrefixes = {};
  11109.     
  11110.     var defaultValue = '${1};';
  11111.     
  11112.     // XXX module preferences
  11113.     var prefs = require('preferences');
  11114.     prefs.define('css.valueSeparator', ': ',
  11115.             'Defines a symbol that should be placed between CSS property and ' 
  11116.             + 'value when expanding CSS abbreviations.');
  11117.     prefs.define('css.propertyEnd', ';',
  11118.             'Defines a symbol that should be placed at the end of CSS property  ' 
  11119.             + 'when expanding CSS abbreviations.');
  11120.     
  11121.     prefs.define('stylus.valueSeparator', ' ',
  11122.             'Defines a symbol that should be placed between CSS property and ' 
  11123.             + 'value when expanding CSS abbreviations in Stylus dialect.');
  11124.     prefs.define('stylus.propertyEnd', '',
  11125.             'Defines a symbol that should be placed at the end of CSS property  ' 
  11126.             + 'when expanding CSS abbreviations in Stylus dialect.');
  11127.     
  11128.     prefs.define('sass.propertyEnd', '',
  11129.             'Defines a symbol that should be placed at the end of CSS property  ' 
  11130.             + 'when expanding CSS abbreviations in SASS dialect.');
  11131.     
  11132.     prefs.define('css.autoInsertVendorPrefixes', true,
  11133.             'Automatically generate vendor-prefixed copies of expanded CSS ' 
  11134.             + 'property. By default, Emmet will generate vendor-prefixed '
  11135.             + 'properties only when you put dash before abbreviation ' 
  11136.             + '(e.g. <code>-bxsh</code>). With this option enabled, you donΓÇÖt ' 
  11137.             + 'need dashes before abbreviations: Emmet will produce ' 
  11138.             + 'vendor-prefixed properties for you.');
  11139.     
  11140.     var descTemplate = _.template('A comma-separated list of CSS properties that may have ' 
  11141.         + '<code><%= vendor %></code> vendor prefix. This list is used to generate '
  11142.         + 'a list of prefixed properties when expanding <code>-property</code> '
  11143.         + 'abbreviations. Empty list means that all possible CSS values may ' 
  11144.         + 'have <code><%= vendor %></code> prefix.');
  11145.     
  11146.     var descAddonTemplate = _.template('A comma-separated list of <em>additional</em> CSS properties ' 
  11147.             + 'for <code>css.<%= vendor %>Preperties</code> preference. ' 
  11148.             + 'You should use this list if you want to add or remove a few CSS ' 
  11149.             + 'properties to original set. To add a new property, simply write its name, '
  11150.             + 'to remove it, precede property with hyphen.<br>'
  11151.             + 'For example, to add <em>foo</em> property and remove <em>border-radius</em> one, '
  11152.             + 'the preference value will look like this: <code>foo, -border-radius</code>.');
  11153.     
  11154.     // properties list is created from cssFeatures.html file
  11155.     var props = {
  11156.         'webkit': 'animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-clip, background-composite, background-origin, background-size, border-fit, border-horizontal-spacing, border-image, border-vertical-spacing, box-align, box-direction, box-flex, box-flex-group, box-lines, box-ordinal-group, box-orient, box-pack, box-reflect, box-shadow, color-correction, column-break-after, column-break-before, column-break-inside, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-span, column-width, dashboard-region, font-smoothing, highlight, hyphenate-character, hyphenate-limit-after, hyphenate-limit-before, hyphens, line-box-contain, line-break, line-clamp, locale, margin-before-collapse, margin-after-collapse, marquee-direction, marquee-increment, marquee-repetition, marquee-style, mask-attachment, mask-box-image, mask-box-image-outset, mask-box-image-repeat, mask-box-image-slice, mask-box-image-source, mask-box-image-width, mask-clip, mask-composite, mask-image, mask-origin, mask-position, mask-repeat, mask-size, nbsp-mode, perspective, perspective-origin, rtl-ordering, text-combine, text-decorations-in-effect, text-emphasis-color, text-emphasis-position, text-emphasis-style, text-fill-color, text-orientation, text-security, text-stroke-color, text-stroke-width, transform, transition, transform-origin, transform-style, transition-delay, transition-duration, transition-property, transition-timing-function, user-drag, user-modify, user-select, writing-mode, svg-shadow, box-sizing, border-radius',
  11157.         'moz': 'animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-inline-policy, binding, border-bottom-colors, border-image, border-left-colors, border-right-colors, border-top-colors, box-align, box-direction, box-flex, box-ordinal-group, box-orient, box-pack, box-shadow, box-sizing, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-width, float-edge, font-feature-settings, font-language-override, force-broken-image-icon, hyphens, image-region, orient, outline-radius-bottomleft, outline-radius-bottomright, outline-radius-topleft, outline-radius-topright, perspective, perspective-origin, stack-sizing, tab-size, text-blink, text-decoration-color, text-decoration-line, text-decoration-style, text-size-adjust, transform, transform-origin, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-focus, user-input, user-modify, user-select, window-shadow, background-clip, border-radius',
  11158.         'ms': 'accelerator, backface-visibility, background-position-x, background-position-y, behavior, block-progression, box-align, box-direction, box-flex, box-line-progression, box-lines, box-ordinal-group, box-orient, box-pack, content-zoom-boundary, content-zoom-boundary-max, content-zoom-boundary-min, content-zoom-chaining, content-zoom-snap, content-zoom-snap-points, content-zoom-snap-type, content-zooming, filter, flow-from, flow-into, font-feature-settings, grid-column, grid-column-align, grid-column-span, grid-columns, grid-layer, grid-row, grid-row-align, grid-row-span, grid-rows, high-contrast-adjust, hyphenate-limit-chars, hyphenate-limit-lines, hyphenate-limit-zone, hyphens, ime-mode, interpolation-mode, layout-flow, layout-grid, layout-grid-char, layout-grid-line, layout-grid-mode, layout-grid-type, line-break, overflow-style, perspective, perspective-origin, perspective-origin-x, perspective-origin-y, scroll-boundary, scroll-boundary-bottom, scroll-boundary-left, scroll-boundary-right, scroll-boundary-top, scroll-chaining, scroll-rails, scroll-snap-points-x, scroll-snap-points-y, scroll-snap-type, scroll-snap-x, scroll-snap-y, scrollbar-arrow-color, scrollbar-base-color, scrollbar-darkshadow-color, scrollbar-face-color, scrollbar-highlight-color, scrollbar-shadow-color, scrollbar-track-color, text-align-last, text-autospace, text-justify, text-kashida-space, text-overflow, text-size-adjust, text-underline-position, touch-action, transform, transform-origin, transform-origin-x, transform-origin-y, transform-origin-z, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-select, word-break, word-wrap, wrap-flow, wrap-margin, wrap-through, writing-mode',
  11159.         'o': 'dashboard-region, animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, border-image, link, link-source, object-fit, object-position, tab-size, table-baseline, transform, transform-origin, transition, transition-delay, transition-duration, transition-property, transition-timing-function, accesskey, input-format, input-required, marquee-dir, marquee-loop, marquee-speed, marquee-style'
  11160.     };
  11161.     
  11162.     _.each(props, function(v, k) {
  11163.         prefs.define('css.' + k + 'Properties', v, descTemplate({vendor: k}));
  11164.         prefs.define('css.' + k + 'PropertiesAddon', '', descAddonTemplate({vendor: k}));
  11165.     });
  11166.     
  11167.     prefs.define('css.unitlessProperties', 'z-index, line-height, opacity, font-weight, zoom', 
  11168.             'The list of properties whose values ΓÇïΓÇïmust not contain units.');
  11169.     
  11170.     prefs.define('css.intUnit', 'px', 'Default unit for integer values');
  11171.     prefs.define('css.floatUnit', 'em', 'Default unit for float values');
  11172.     
  11173.     prefs.define('css.keywords', 'auto, inherit', 
  11174.             'A comma-separated list of valid keywords that can be used in CSS abbreviations.');
  11175.     
  11176.     prefs.define('css.keywordAliases', 'a:auto, i:inherit, s:solid, da:dashed, do:dotted', 
  11177.             'A comma-separated list of keyword aliases, used in CSS abbreviation. '
  11178.             + 'Each alias should be defined as <code>alias:keyword_name</code>.');
  11179.     
  11180.     prefs.define('css.unitAliases', 'e:em, p:%, x:ex, r:rem', 
  11181.             'A comma-separated list of unit aliases, used in CSS abbreviation. '
  11182.             + 'Each alias should be defined as <code>alias:unit_value</code>.');
  11183.     
  11184.     prefs.define('css.color.short', true, 
  11185.             'Should color values like <code>#ffffff</code> be shortened to '
  11186.             + '<code>#fff</code> after abbreviation with color was expanded.');
  11187.     
  11188.     prefs.define('css.color.case', 'keep', 
  11189.             'Letter case of color values generated by abbreviations with color '
  11190.             + '(like <code>c#0</code>). Possible values are <code>upper</code>, '
  11191.             + '<code>lower</code> and <code>keep</code>.');
  11192.     
  11193.     prefs.define('css.fuzzySearch', true, 
  11194.             'Enable fuzzy search among CSS snippet names. When enabled, every ' 
  11195.             + '<em>unknown</em> snippet will be scored against available snippet '
  11196.             + 'names (not values or CSS properties!). The match with best score '
  11197.             + 'will be used to resolve snippet value. For example, with this ' 
  11198.             + 'preference enabled, the following abbreviations are equal: '
  11199.             + '<code>ov:h</code> == <code>ov-h</code> == <code>o-h</code> == '
  11200.             + '<code>oh</code>');
  11201.     
  11202.     prefs.define('css.fuzzySearchMinScore', 0.3, 
  11203.             'The minium score (from 0 to 1) that fuzzy-matched abbreviation should ' 
  11204.             + 'achive. Lower values may produce many false-positive matches, '
  11205.             + 'higher values may reduce possible matches.');
  11206.     
  11207.     
  11208.     function isNumeric(ch) {
  11209.         var code = ch && ch.charCodeAt(0);
  11210.         return (ch && ch == '.' || (code > 47 && code < 58));
  11211.     }
  11212.     
  11213.     /**
  11214.      * Check if provided snippet contains only one CSS property and value.
  11215.      * @param {String} snippet
  11216.      * @returns {Boolean}
  11217.      */
  11218.     function isSingleProperty(snippet) {
  11219.         var utils = require('utils');
  11220.         snippet = utils.trim(snippet);
  11221.         
  11222.         // check if it doesn't contain a comment and a newline
  11223.         if (~snippet.indexOf('/*') || /[\n\r]/.test(snippet)) {
  11224.             return false;
  11225.         }
  11226.         
  11227.         // check if it's a valid snippet definition
  11228.         if (!/^[a-z0-9\-]+\s*\:/i.test(snippet)) {
  11229.             return false;
  11230.         }
  11231.         
  11232.         snippet = require('tabStops').processText(snippet, {
  11233.             replaceCarets: true,
  11234.             tabstop: function() {
  11235.                 return 'value';
  11236.             }
  11237.         });
  11238.         
  11239.         return snippet.split(':').length == 2;
  11240.     }
  11241.     
  11242.     /**
  11243.      * Normalizes abbreviated value to final CSS one
  11244.      * @param {String} value
  11245.      * @returns {String}
  11246.      */
  11247.     function normalizeValue(value) {
  11248.         if (value.charAt(0) == '-' && !/^\-[\.\d]/.test(value)) {
  11249.             value = value.replace(/^\-+/, '');
  11250.         }
  11251.         
  11252.         if (value.charAt(0) == '#') {
  11253.             return normalizeHexColor(value);
  11254.         }
  11255.         
  11256.         return getKeyword(value);
  11257.     }
  11258.     
  11259.     function normalizeHexColor(value) {
  11260.         var hex = value.replace(/^#+/, '') || '0';
  11261.         var repeat = require('utils').repeatString;
  11262.         var color = null;
  11263.         switch (hex.length) {
  11264.             case 1:
  11265.                 color = repeat(hex, 6);
  11266.                 break;
  11267.             case 2:
  11268.                 color = repeat(hex, 3);
  11269.                 break;
  11270.             case 3:
  11271.                 color = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
  11272.                 break;
  11273.             case 4:
  11274.                 color = hex + hex.substr(0, 2);
  11275.                 break;
  11276.             case 5:
  11277.                 color = hex + hex.charAt(0);
  11278.                 break;
  11279.             default:
  11280.                 color = hex.substr(0, 6);
  11281.         }
  11282.         
  11283.         // color must be shortened?
  11284.         if (prefs.get('css.color.short')) {
  11285.             var p = color.split('');
  11286.             if (p[0] == p[1] && p[2] == p[3] && p[4] == p[5]) {
  11287.                 color = p[0] + p[2] + p[4];
  11288.             }
  11289.         }
  11290.         
  11291.         // should transform case?
  11292.         switch (prefs.get('css.color.case')) {
  11293.             case 'upper':
  11294.                 color = color.toUpperCase();
  11295.                 break;
  11296.             case 'lower':
  11297.                 color = color.toLowerCase();
  11298.                 break;
  11299.         }
  11300.         
  11301.         return '#' + color;
  11302.     }
  11303.     
  11304.     function getKeyword(name) {
  11305.         var aliases = prefs.getDict('css.keywordAliases');
  11306.         return name in aliases ? aliases[name] : name;
  11307.     }
  11308.     
  11309.     function getUnit(name) {
  11310.         var aliases = prefs.getDict('css.unitAliases');
  11311.         return name in aliases ? aliases[name] : name;
  11312.     }
  11313.     
  11314.     function isValidKeyword(keyword) {
  11315.         return _.include(prefs.getArray('css.keywords'), getKeyword(keyword));
  11316.     }
  11317.     
  11318.     /**
  11319.      * Split snippet into a CSS property-value pair
  11320.      * @param {String} snippet
  11321.      */
  11322.     function splitSnippet(snippet) {
  11323.         var utils = require('utils');
  11324.         snippet = utils.trim(snippet);
  11325.         if (snippet.indexOf(':') == -1) {
  11326.             return {
  11327.                 name: snippet,
  11328.                 value: defaultValue
  11329.             };
  11330.         }
  11331.         
  11332.         var pair = snippet.split(':');
  11333.         
  11334.         return {
  11335.             name: utils.trim(pair.shift()),
  11336.             // replace ${0} tabstop to produce valid vendor-prefixed values
  11337.             // where possible
  11338.             value: utils.trim(pair.join(':')).replace(/^(\$\{0\}|\$0)(\s*;?)$/, '${1}$2')
  11339.         };
  11340.     }
  11341.     
  11342.     /**
  11343.      * Check if passed CSS property support specified vendor prefix 
  11344.      * @param {String} property
  11345.      * @param {String} prefix
  11346.      */
  11347.     function hasPrefix(property, prefix) {
  11348.         var info = vendorPrefixes[prefix];
  11349.         
  11350.         if (!info)
  11351.             info = _.find(vendorPrefixes, function(data) {
  11352.                 return data.prefix == prefix;
  11353.             });
  11354.         
  11355.         return info && info.supports(property);
  11356.     }
  11357.     
  11358.     /**
  11359.      * Search for a list of supported prefixes for CSS property. This list
  11360.      * is used to generate all-prefixed snippet
  11361.      * @param {String} property CSS property name
  11362.      * @returns {Array}
  11363.      */
  11364.     function findPrefixes(property, noAutofill) {
  11365.         var result = [];
  11366.         _.each(vendorPrefixes, function(obj, prefix) {
  11367.             if (hasPrefix(property, prefix)) {
  11368.                 result.push(prefix);
  11369.             }
  11370.         });
  11371.         
  11372.         if (!result.length && !noAutofill) {
  11373.             // add all non-obsolete prefixes
  11374.             _.each(vendorPrefixes, function(obj, prefix) {
  11375.                 if (!obj.obsolete)
  11376.                     result.push(prefix);
  11377.             });
  11378.         }
  11379.         
  11380.         return result;
  11381.     }
  11382.     
  11383.     function addPrefix(name, obj) {
  11384.         if (_.isString(obj))
  11385.             obj = {prefix: obj};
  11386.         
  11387.         vendorPrefixes[name] = _.extend({}, prefixObj, obj);
  11388.     }
  11389.     
  11390.     function getSyntaxPreference(name, syntax) {
  11391.         if (syntax) {
  11392.             var val = prefs.get(syntax + '.' + name);
  11393.             if (!_.isUndefined(val))
  11394.                 return val;
  11395.         }
  11396.         
  11397.         return prefs.get('css.' + name);
  11398.     }
  11399.     
  11400.     /**
  11401.      * Format CSS property according to current syntax dialect
  11402.      * @param {String} property
  11403.      * @param {String} syntax
  11404.      * @returns {String}
  11405.      */
  11406.     function formatProperty(property, syntax) {
  11407.         var ix = property.indexOf(':');
  11408.         property = property.substring(0, ix).replace(/\s+$/, '') 
  11409.             + getSyntaxPreference('valueSeparator', syntax)
  11410.             + require('utils').trim(property.substring(ix + 1));
  11411.         
  11412.         return property.replace(/\s*;\s*$/, getSyntaxPreference('propertyEnd', syntax));
  11413.     }
  11414.     
  11415.     /**
  11416.      * Transforms snippet value if required. For example, this transformation
  11417.      * may add <i>!important</i> declaration to CSS property
  11418.      * @param {String} snippet
  11419.      * @param {Boolean} isImportant
  11420.      * @returns {String}
  11421.      */
  11422.     function transformSnippet(snippet, isImportant, syntax) {
  11423.         if (!_.isString(snippet))
  11424.             snippet = snippet.data;
  11425.         
  11426.         if (!isSingleProperty(snippet))
  11427.             return snippet;
  11428.         
  11429.         if (isImportant) {
  11430.             if (~snippet.indexOf(';')) {
  11431.                 snippet = snippet.split(';').join(' !important;');
  11432.             } else {
  11433.                 snippet += ' !important';
  11434.             }
  11435.         }
  11436.         
  11437.         return formatProperty(snippet, syntax);
  11438.     }
  11439.     
  11440.     /**
  11441.      * Helper function that parses comma-separated list of elements into array
  11442.      * @param {String} list
  11443.      * @returns {Array}
  11444.      */
  11445.     function parseList(list) {
  11446.         var result = _.map((list || '').split(','), require('utils').trim);
  11447.         return result.length ? result : null;
  11448.     }
  11449.     
  11450.     function getProperties(key) {
  11451.         var list = prefs.getArray(key);
  11452.         _.each(prefs.getArray(key + 'Addon'), function(prop) {
  11453.             if (prop.charAt(0) == '-') {
  11454.                 list = _.without(list, prop.substr(1));
  11455.             } else {
  11456.                 if (prop.charAt(0) == '+')
  11457.                     prop = prop.substr(1);
  11458.                 
  11459.                 list.push(prop);
  11460.             }
  11461.         });
  11462.         
  11463.         return list;
  11464.     }
  11465.     
  11466.     
  11467.     // TODO refactor, this looks awkward now
  11468.     addPrefix('w', {
  11469.         prefix: 'webkit'
  11470.     });
  11471.     addPrefix('m', {
  11472.         prefix: 'moz'
  11473.     });
  11474.     addPrefix('s', {
  11475.         prefix: 'ms'
  11476.     });
  11477.     addPrefix('o', {
  11478.         prefix: 'o'
  11479.     });
  11480.     
  11481.     // I think nobody uses it
  11482. //    addPrefix('k', {
  11483. //        prefix: 'khtml',
  11484. //        obsolete: true
  11485. //    });
  11486.     
  11487.     var cssSyntaxes = ['css', 'less', 'sass', 'scss', 'stylus'];
  11488.     
  11489.     /**
  11490.      * XXX register resolver
  11491.      * @param {TreeNode} node
  11492.      * @param {String} syntax
  11493.      */
  11494.     require('resources').addResolver(function(node, syntax) {
  11495.         if (_.include(cssSyntaxes, syntax) && node.isElement()) {
  11496.             return module.expandToSnippet(node.abbreviation, syntax);
  11497.         }
  11498.         
  11499.         return null;
  11500.     });
  11501.     
  11502.     var ea = require('expandAbbreviation');
  11503.     /**
  11504.      * For CSS-like syntaxes, we need to handle a special use case. Some editors
  11505.      * (like Sublime Text 2) may insert semicolons automatically when user types
  11506.      * abbreviation. After expansion, user receives a double semicolon. This
  11507.      * handler automatically removes semicolon from generated content in such cases.
  11508.      * @param {IEmmetEditor} editor
  11509.      * @param {String} syntax
  11510.      * @param {String} profile
  11511.      */
  11512.     ea.addHandler(function(editor, syntax, profile) {
  11513.         if (!_.include(cssSyntaxes, syntax)) {
  11514.             return false;
  11515.         }
  11516.         
  11517.         var caretPos = editor.getSelectionRange().end;
  11518.         var abbr = ea.findAbbreviation(editor);
  11519.             
  11520.         if (abbr) {
  11521.             var content = emmet.expandAbbreviation(abbr, syntax, profile);
  11522.             if (content) {
  11523.                 var replaceFrom = caretPos - abbr.length;
  11524.                 var replaceTo = caretPos;
  11525.                 if (editor.getContent().charAt(caretPos) == ';' && content.charAt(content.length - 1) == ';') {
  11526.                     replaceTo++;
  11527.                 }
  11528.                 
  11529.                 editor.replaceContent(content, replaceFrom, replaceTo);
  11530.                 return true;
  11531.             }
  11532.         }
  11533.         
  11534.         return false;
  11535.     });
  11536.     
  11537.     return module = {
  11538.         /**
  11539.          * Adds vendor prefix
  11540.          * @param {String} name One-character prefix name
  11541.          * @param {Object} obj Object describing vendor prefix
  11542.          * @memberOf cssResolver
  11543.          */
  11544.         addPrefix: addPrefix,
  11545.         
  11546.         /**
  11547.          * Check if passed CSS property supports specified vendor prefix
  11548.          * @param {String} property
  11549.          * @param {String} prefix
  11550.          */
  11551.         supportsPrefix: hasPrefix,
  11552.         
  11553.         /**
  11554.          * Returns prefixed version of passed CSS property, only if this
  11555.          * property supports such prefix
  11556.          * @param {String} property
  11557.          * @param {String} prefix
  11558.          * @returns
  11559.          */
  11560.         prefixed: function(property, prefix) {
  11561.             return hasPrefix(property, prefix) 
  11562.                 ? '-' + prefix + '-' + property 
  11563.                 : property;
  11564.         },
  11565.         
  11566.         /**
  11567.          * Returns list of all registered vendor prefixes
  11568.          * @returns {Array}
  11569.          */
  11570.         listPrefixes: function() {
  11571.             return _.map(vendorPrefixes, function(obj) {
  11572.                 return obj.prefix;
  11573.             });
  11574.         },
  11575.         
  11576.         /**
  11577.          * Returns object describing vendor prefix
  11578.          * @param {String} name
  11579.          * @returns {Object}
  11580.          */
  11581.         getPrefix: function(name) {
  11582.             return vendorPrefixes[name];
  11583.         },
  11584.         
  11585.         /**
  11586.          * Removes prefix object
  11587.          * @param {String} name
  11588.          */
  11589.         removePrefix: function(name) {
  11590.             if (name in vendorPrefixes)
  11591.                 delete vendorPrefixes[name];
  11592.         },
  11593.         
  11594.         /**
  11595.          * Extract vendor prefixes from abbreviation
  11596.          * @param {String} abbr
  11597.          * @returns {Object} Object containing array of prefixes and clean 
  11598.          * abbreviation name
  11599.          */
  11600.         extractPrefixes: function(abbr) {
  11601.             if (abbr.charAt(0) != '-') {
  11602.                 return {
  11603.                     property: abbr,
  11604.                     prefixes: null
  11605.                 };
  11606.             }
  11607.             
  11608.             // abbreviation may either contain sequence of one-character prefixes
  11609.             // or just dash, meaning that user wants to produce all possible
  11610.             // prefixed properties
  11611.             var i = 1, il = abbr.length, ch;
  11612.             var prefixes = [];
  11613.             
  11614.             while (i < il) {
  11615.                 ch = abbr.charAt(i);
  11616.                 if (ch == '-') {
  11617.                     // end-sequence character found, stop searching
  11618.                     i++;
  11619.                     break;
  11620.                 }
  11621.                 
  11622.                 if (ch in vendorPrefixes) {
  11623.                     prefixes.push(ch);
  11624.                 } else {
  11625.                     // no prefix found, meaning user want to produce all
  11626.                     // vendor-prefixed properties
  11627.                     prefixes.length = 0;
  11628.                     i = 1;
  11629.                     break;
  11630.                 }
  11631.                 
  11632.                 i++;
  11633.             }
  11634.             
  11635.             // reached end of abbreviation and no property name left
  11636.             if (i == il -1) {
  11637.                 i = 1;
  11638.                 prefixes.length = 1;
  11639.             }
  11640.             
  11641.             return {
  11642.                 property: abbr.substring(i),
  11643.                 prefixes: prefixes.length ? prefixes : 'all'
  11644.             };
  11645.         },
  11646.         
  11647.         /**
  11648.          * Search for value substring in abbreviation
  11649.          * @param {String} abbr
  11650.          * @returns {String} Value substring
  11651.          */
  11652.         findValuesInAbbreviation: function(abbr, syntax) {
  11653.             syntax = syntax || 'css';
  11654.             
  11655.             var i = 0, il = abbr.length, value = '', ch;
  11656.             while (i < il) {
  11657.                 ch = abbr.charAt(i);
  11658.                 if (isNumeric(ch) || ch == '#' || (ch == '-' && isNumeric(abbr.charAt(i + 1)))) {
  11659.                     value = abbr.substring(i);
  11660.                     break;
  11661.                 }
  11662.                 
  11663.                 i++;
  11664.             }
  11665.             
  11666.             // try to find keywords in abbreviation
  11667.             var property = abbr.substring(0, abbr.length - value.length);
  11668.             var res = require('resources');
  11669.             var keywords = [];
  11670.             // try to extract some commonly-used properties
  11671.             while (~property.indexOf('-') && !res.findSnippet(syntax, property)) {
  11672.                 var parts = property.split('-');
  11673.                 var lastPart = parts.pop();
  11674.                 if (!isValidKeyword(lastPart)) {
  11675.                     break;
  11676.                 }
  11677.                 
  11678.                 keywords.unshift(lastPart);
  11679.                 property = parts.join('-');
  11680.             }
  11681.             
  11682.             return keywords.join('-') + value;
  11683.         },
  11684.         
  11685.         parseValues: function(str) {
  11686.             /** @type StringStream */
  11687.             var stream = require('stringStream').create(str);
  11688.             var values = [];
  11689.             var ch = null;
  11690.             
  11691.             while (ch = stream.next()) {
  11692.                 if (ch == '#') {
  11693.                     stream.match(/^[0-9a-f]+/, true);
  11694.                     values.push(stream.current());
  11695.                 } else if (ch == '-') {
  11696.                     if (isValidKeyword(_.last(values)) || 
  11697.                             ( stream.start && isNumeric(str.charAt(stream.start - 1)) )
  11698.                         ) {
  11699.                         stream.start = stream.pos;
  11700.                     }
  11701.                     
  11702.                     stream.match(/^\-?[0-9]*(\.[0-9]+)?[a-z%\.]*/, true);
  11703.                     values.push(stream.current());
  11704.                 } else {
  11705.                     stream.match(/^[0-9]*(\.[0-9]*)?[a-z%]*/, true);
  11706.                     values.push(stream.current());
  11707.                 }
  11708.                 
  11709.                 stream.start = stream.pos;
  11710.             }
  11711.             
  11712.             return _.map(_.compact(values), normalizeValue);
  11713.         },
  11714.         
  11715.         /**
  11716.          * Extracts values from abbreviation
  11717.          * @param {String} abbr
  11718.          * @returns {Object} Object containing array of values and clean 
  11719.          * abbreviation name
  11720.          */
  11721.         extractValues: function(abbr) {
  11722.             // search for value start
  11723.             var abbrValues = this.findValuesInAbbreviation(abbr);
  11724.             if (!abbrValues) {
  11725.                 return {
  11726.                     property: abbr,
  11727.                     values: null
  11728.                 };
  11729.             }
  11730.             
  11731.             return {
  11732.                 property: abbr.substring(0, abbr.length - abbrValues.length).replace(/-$/, ''),
  11733.                 values: this.parseValues(abbrValues)
  11734.             };
  11735.         },
  11736.         
  11737.         /**
  11738.          * Normalizes value, defined in abbreviation.
  11739.          * @param {String} value
  11740.          * @param {String} property
  11741.          * @returns {String}
  11742.          */
  11743.         normalizeValue: function(value, property) {
  11744.             property = (property || '').toLowerCase();
  11745.             var unitlessProps = prefs.getArray('css.unitlessProperties');
  11746.             return value.replace(/^(\-?[0-9\.]+)([a-z]*)$/, function(str, val, unit) {
  11747.                 if (!unit && (val == '0' || _.include(unitlessProps, property)))
  11748.                     return val;
  11749.                 
  11750.                 if (!unit)
  11751.                     return val.replace(/\.$/, '') + prefs.get(~val.indexOf('.') ? 'css.floatUnit' : 'css.intUnit');
  11752.                 
  11753.                 return val + getUnit(unit);
  11754.             });
  11755.         },
  11756.         
  11757.         /**
  11758.          * Expands abbreviation into a snippet
  11759.          * @param {String} abbr Abbreviation name to expand
  11760.          * @param {String} value Abbreviation value
  11761.          * @param {String} syntax Currect syntax or dialect. Default is 'css'
  11762.          * @returns {Object} Array of CSS properties and values or predefined
  11763.          * snippet (string or element)
  11764.          */
  11765.         expand: function(abbr, value, syntax) {
  11766.             syntax = syntax || 'css';
  11767.             var resources = require('resources');
  11768.             var autoInsertPrefixes = prefs.get('css.autoInsertVendorPrefixes');
  11769.             
  11770.             // check if snippet should be transformed to !important
  11771.             var isImportant;
  11772.             if (isImportant = /^(.+)\!$/.test(abbr)) {
  11773.                 abbr = RegExp.$1;
  11774.             }
  11775.             
  11776.             // check if we have abbreviated resource
  11777.             var snippet = resources.findSnippet(syntax, abbr);
  11778.             if (snippet && !autoInsertPrefixes) {
  11779.                 return transformSnippet(snippet, isImportant, syntax);
  11780.             }
  11781.             
  11782.             // no abbreviated resource, parse abbreviation
  11783.             var prefixData = this.extractPrefixes(abbr);
  11784.             var valuesData = this.extractValues(prefixData.property);
  11785.             var abbrData = _.extend(prefixData, valuesData);
  11786.             
  11787.             snippet = resources.findSnippet(syntax, abbrData.property);
  11788.             
  11789.             // fallback to some old snippets like m:a
  11790. //            if (!snippet && ~abbrData.property.indexOf(':')) {
  11791. //                var parts = abbrData.property.split(':');
  11792. //                var propertyName = parts.shift();
  11793. //                snippet = resources.findSnippet(syntax, propertyName) || propertyName;
  11794. //                abbrData.values = this.parseValues(parts.join(':'));
  11795. //            }
  11796.             
  11797.             if (!snippet && prefs.get('css.fuzzySearch')) {
  11798.                 // letΓÇÖs try fuzzy search
  11799.                 snippet = resources.fuzzyFindSnippet(syntax, abbrData.property, parseFloat(prefs.get('css.fuzzySearchMinScore')));
  11800.             }
  11801.             
  11802.             if (!snippet) {
  11803.                 snippet = abbrData.property + ':' + defaultValue;
  11804.             } else if (!_.isString(snippet)) {
  11805.                 snippet = snippet.data;
  11806.             }
  11807.             
  11808.             if (!isSingleProperty(snippet)) {
  11809.                 return snippet;
  11810.             }
  11811.             
  11812.             var snippetObj = splitSnippet(snippet);
  11813.             var result = [];
  11814.             if (!value && abbrData.values) {
  11815.                 value = _.map(abbrData.values, function(val) {
  11816.                     return this.normalizeValue(val, snippetObj.name);
  11817.                 }, this).join(' ') + ';';
  11818.             }
  11819.             
  11820.             snippetObj.value = value || snippetObj.value;
  11821.             
  11822.             var prefixes = abbrData.prefixes == 'all' || (!abbrData.prefixes && autoInsertPrefixes) 
  11823.                 ? findPrefixes(snippetObj.name, autoInsertPrefixes && abbrData.prefixes != 'all')
  11824.                 : abbrData.prefixes;
  11825.                 
  11826.             _.each(prefixes, function(p) {
  11827.                 if (p in vendorPrefixes) {
  11828.                     result.push(transformSnippet(
  11829.                             vendorPrefixes[p].transformName(snippetObj.name) 
  11830.                             + ':' + snippetObj.value,
  11831.                             isImportant, syntax));
  11832.                     
  11833.                 }
  11834.             });
  11835.             
  11836.             // put the original property
  11837.             result.push(transformSnippet(snippetObj.name + ':' + snippetObj.value, isImportant, syntax));
  11838.             
  11839.             return result;
  11840.         },
  11841.         
  11842.         /**
  11843.          * Same as <code>expand</code> method but transforms output into 
  11844.          * Emmet snippet
  11845.          * @param {String} abbr
  11846.          * @param {String} syntax
  11847.          * @returns {String}
  11848.          */
  11849.         expandToSnippet: function(abbr, syntax) {
  11850.             var snippet = this.expand(abbr, null, syntax);
  11851.             if (_.isArray(snippet)) {
  11852.                 return snippet.join('\n');
  11853.             }
  11854.             
  11855.             if (!_.isString(snippet))
  11856.                 return snippet.data;
  11857.             
  11858.             return String(snippet);
  11859.         },
  11860.         
  11861.         getSyntaxPreference: getSyntaxPreference,
  11862.         transformSnippet: transformSnippet
  11863.     };
  11864. });
  11865. /**
  11866.  * 'Expand Abbreviation' handler that parses gradient definition from under 
  11867.  * cursor and updates CSS rule with vendor-prefixed values.
  11868.  * 
  11869.  * @memberOf __cssGradientHandlerDefine
  11870.  * @param {Function} require
  11871.  * @param {Underscore} _
  11872.  */
  11873. emmet.define('cssGradient', function(require, _) {
  11874.     var defaultLinearDirections = ['top', 'to bottom', '0deg'];
  11875.     /** Back-reference to current module */
  11876.     var module = null;
  11877.     
  11878.     var cssSyntaxes = ['css', 'less', 'sass', 'scss', 'stylus', 'styl'];
  11879.     
  11880.     var reDeg = /\d+deg/i;
  11881.     var reKeyword = /top|bottom|left|right/i;
  11882.     
  11883.     // XXX define preferences
  11884.     /** @type preferences */
  11885.     var prefs = require('preferences');
  11886.     prefs.define('css.gradient.prefixes', 'webkit, moz, o',
  11887.             'A comma-separated list of vendor-prefixes for which values should ' 
  11888.             + 'be generated.');
  11889.     
  11890.     prefs.define('css.gradient.oldWebkit', true,
  11891.             'Generate gradient definition for old Webkit implementations');
  11892.     
  11893.     prefs.define('css.gradient.omitDefaultDirection', true,
  11894.         'Do not output default direction definition in generated gradients.');
  11895.     
  11896.     prefs.define('css.gradient.defaultProperty', 'background-image',
  11897.         'When gradient expanded outside CSS value context, it will produce '
  11898.             + 'properties with this name.');
  11899.     
  11900.     prefs.define('css.gradient.fallback', false,
  11901.             'With this option enabled, CSS gradient generator will produce '
  11902.             + '<code>background-color</code> property with gradient first color '
  11903.             + 'as fallback for old browsers.');
  11904.     
  11905.     function normalizeSpace(str) {
  11906.         return require('utils').trim(str).replace(/\s+/g, ' ');
  11907.     }
  11908.     
  11909.     /**
  11910.      * Parses linear gradient definition
  11911.      * @param {String}
  11912.      */
  11913.     function parseLinearGradient(gradient) {
  11914.         var direction = defaultLinearDirections[0];
  11915.         
  11916.         // extract tokens
  11917.         /** @type StringStream */
  11918.         var stream = require('stringStream').create(require('utils').trim(gradient));
  11919.         var colorStops = [], ch;
  11920.         while (ch = stream.next()) {
  11921.             if (stream.peek() == ',') {
  11922.                 colorStops.push(stream.current());
  11923.                 stream.next();
  11924.                 stream.eatSpace();
  11925.                 stream.start = stream.pos;
  11926.             } else if (ch == '(') { // color definition, like 'rgb(0,0,0)'
  11927.                 stream.skipTo(')');
  11928.             }
  11929.         }
  11930.         
  11931.         // add last token
  11932.         colorStops.push(stream.current());
  11933.         colorStops = _.compact(_.map(colorStops, normalizeSpace));
  11934.         
  11935.         if (!colorStops.length)
  11936.             return null;
  11937.         
  11938.         // let's see if the first color stop is actually a direction
  11939.         if (reDeg.test(colorStops[0]) || reKeyword.test(colorStops[0])) {
  11940.             direction = colorStops.shift();
  11941.         }
  11942.         
  11943.         return {
  11944.             type: 'linear',
  11945.             direction: direction,
  11946.             colorStops: _.map(colorStops, parseColorStop)
  11947.         };
  11948.     }
  11949.     
  11950.     /**
  11951.      * Parses color stop definition
  11952.      * @param {String} colorStop
  11953.      * @returns {Object}
  11954.      */
  11955.     function parseColorStop(colorStop) {
  11956.         colorStop = normalizeSpace(colorStop);
  11957.         
  11958.         // find color declaration
  11959.         // first, try complex color declaration, like rgb(0,0,0)
  11960.         var color = null;
  11961.         colorStop = colorStop.replace(/^(\w+\(.+?\))\s*/, function(str, c) {
  11962.             color = c;
  11963.             return '';
  11964.         });
  11965.         
  11966.         if (!color) {
  11967.             // try simple declaration, like yellow, #fco, #ffffff, etc.
  11968.             var parts = colorStop.split(' ');
  11969.             color = parts[0];
  11970.             colorStop = parts[1] || '';
  11971.         }
  11972.         
  11973.         var result = {
  11974.             color: color
  11975.         };
  11976.         
  11977.         if (colorStop) {
  11978.             // there's position in color stop definition
  11979.             colorStop.replace(/^(\-?[\d\.]+)([a-z%]+)?$/, function(str, pos, unit) {
  11980.                 result.position = pos;
  11981.                 if (~pos.indexOf('.')) {
  11982.                     unit = '';
  11983.                 } else if (!unit) {
  11984.                     unit = '%';
  11985.                 }
  11986.                 
  11987.                 if (unit)
  11988.                     result.unit = unit;
  11989.             });
  11990.         }
  11991.         
  11992.         return result;
  11993.     }
  11994.     
  11995.     /**
  11996.      * Fills-out implied positions in color-stops. This function is useful for
  11997.      * old Webkit gradient definitions
  11998.      */
  11999.     function fillImpliedPositions(colorStops) {
  12000.         var from = 0;
  12001.         
  12002.         _.each(colorStops, function(cs, i) {
  12003.             // make sure that first and last positions are defined
  12004.             if (!i)
  12005.                 return cs.position = cs.position || 0;
  12006.             
  12007.             if (i == colorStops.length - 1 && !('position' in cs))
  12008.                 cs.position = 1;
  12009.             
  12010.             if ('position' in cs) {
  12011.                 var start = colorStops[from].position || 0;
  12012.                 var step = (cs.position - start) / (i - from);
  12013.                 _.each(colorStops.slice(from, i), function(cs2, j) {
  12014.                     cs2.position = start + step * j;
  12015.                 });
  12016.                 
  12017.                 from = i;
  12018.             }
  12019.         });
  12020.     }
  12021.     
  12022.     /**
  12023.      * Returns textual version of direction expressed in degrees
  12024.      * @param {String} direction
  12025.      * @returns {String}
  12026.      */
  12027.     function textualDirection(direction) {
  12028.         var angle = parseFloat(direction);
  12029.         
  12030.         if(!_.isNaN(angle)) {
  12031.             switch(angle % 360) {
  12032.                 case 0:   return 'left';
  12033.                 case 90:  return 'bottom';
  12034.                 case 180: return 'right';
  12035.                 case 240: return 'top';
  12036.             }
  12037.         }
  12038.         
  12039.         return direction;
  12040.     }
  12041.     
  12042.     /**
  12043.      * Creates direction definition for old Webkit gradients
  12044.      * @param {String} direction
  12045.      * @returns {String}
  12046.      */
  12047.     function oldWebkitDirection(direction) {
  12048.         direction = textualDirection(direction);
  12049.         
  12050.         if(reDeg.test(direction))
  12051.             throw "The direction is an angle that canΓÇÖt be converted.";
  12052.         
  12053.         var v = function(pos) {
  12054.             return ~direction.indexOf(pos) ? '100%' : '0';
  12055.         };
  12056.         
  12057.         return v('right') + ' ' + v('bottom') + ', ' + v('left') + ' ' + v('top');
  12058.     }
  12059.     
  12060.     function getPrefixedNames(name) {
  12061.         var prefixes = prefs.getArray('css.gradient.prefixes');
  12062.         var names = _.map(prefixes, function(p) {
  12063.             return '-' + p + '-' + name;
  12064.         });
  12065.         names.push(name);
  12066.         
  12067.         return names;
  12068.     }
  12069.     
  12070.     /**
  12071.      * Returns list of CSS properties with gradient
  12072.      * @param {Object} gradient
  12073.      * @param {String} propertyName Original CSS property name
  12074.      * @returns {Array}
  12075.      */
  12076.     function getPropertiesForGradient(gradient, propertyName) {
  12077.         var props = [];
  12078.         var css = require('cssResolver');
  12079.         
  12080.         if (prefs.get('css.gradient.fallback') && ~propertyName.toLowerCase().indexOf('background')) {
  12081.             props.push({
  12082.                 name: 'background-color',
  12083.                 value: '${1:' + gradient.colorStops[0].color + '}'
  12084.             });
  12085.         }
  12086.         
  12087.         _.each(prefs.getArray('css.gradient.prefixes'), function(prefix) {
  12088.             var name = css.prefixed(propertyName, prefix);
  12089.             if (prefix == 'webkit' && prefs.get('css.gradient.oldWebkit')) {
  12090.                 try {
  12091.                     props.push({
  12092.                         name: name,
  12093.                         value: module.oldWebkitLinearGradient(gradient)
  12094.                     });
  12095.                 } catch(e) {}
  12096.             }
  12097.             
  12098.             props.push({
  12099.                 name: name,
  12100.                 value: module.toString(gradient, prefix)
  12101.             });
  12102.         });
  12103.         
  12104.         return props.sort(function(a, b) {
  12105.             return b.name.length - a.name.length;
  12106.         });
  12107.     }
  12108.     
  12109.     /**
  12110.      * Pastes gradient definition into CSS rule with correct vendor-prefixes
  12111.      * @param {EditElement} property Matched CSS property
  12112.      * @param {Object} gradient Parsed gradient
  12113.      * @param {Range} valueRange If passed, only this range within property 
  12114.      * value will be replaced with gradient. Otherwise, full value will be 
  12115.      * replaced
  12116.      */
  12117.     function pasteGradient(property, gradient, valueRange) {
  12118.         var rule = property.parent;
  12119.         var utils = require('utils');
  12120.         
  12121.         // first, remove all properties within CSS rule with the same name and
  12122.         // gradient definition
  12123.         _.each(rule.getAll(getPrefixedNames(property.name())), function(item) {
  12124.             if (item != property && /gradient/i.test(item.value())) {
  12125.                 rule.remove(item);
  12126.             }
  12127.         });
  12128.         
  12129.         var value = property.value();
  12130.         if (!valueRange)
  12131.             valueRange = require('range').create(0, property.value());
  12132.         
  12133.         var val = function(v) {
  12134.             return utils.replaceSubstring(value, v, valueRange);
  12135.         };
  12136.         
  12137.         // put vanilla-clean gradient definition into current rule
  12138.         property.value(val(module.toString(gradient)) + '${2}');
  12139.         
  12140.         // create list of properties to insert
  12141.         var propsToInsert = getPropertiesForGradient(gradient, property.name());
  12142.         
  12143.         // put vendor-prefixed definitions before current rule
  12144.         _.each(propsToInsert, function(prop) {
  12145.             rule.add(prop.name, prop.value, rule.indexOf(property));
  12146.         });
  12147.     }
  12148.     
  12149.     /**
  12150.      * Search for gradient definition inside CSS property value
  12151.      */
  12152.     function findGradient(cssProp) {
  12153.         var value = cssProp.value();
  12154.         var gradient = null;
  12155.         var matchedPart = _.find(cssProp.valueParts(), function(part) {
  12156.             return gradient = module.parse(part.substring(value));
  12157.         });
  12158.         
  12159.         if (matchedPart && gradient) {
  12160.             return {
  12161.                 gradient: gradient,
  12162.                 valueRange: matchedPart
  12163.             };
  12164.         }
  12165.         
  12166.         return null;
  12167.     }
  12168.     
  12169.     /**
  12170.      * Tries to expand gradient outside CSS value 
  12171.      * @param {IEmmetEditor} editor
  12172.      * @param {String} syntax
  12173.      */
  12174.     function expandGradientOutsideValue(editor, syntax) {
  12175.         var propertyName = prefs.get('css.gradient.defaultProperty');
  12176.         
  12177.         if (!propertyName)
  12178.             return false;
  12179.         
  12180.         // assuming that gradient definition is written on new line,
  12181.         // do a simplified parsing
  12182.         var content = String(editor.getContent());
  12183.         /** @type Range */
  12184.         var lineRange = require('range').create(editor.getCurrentLineRange());
  12185.         
  12186.         // get line content and adjust range with padding
  12187.         var line = lineRange.substring(content)
  12188.             .replace(/^\s+/, function(pad) {
  12189.                 lineRange.start += pad.length;
  12190.                 return '';
  12191.             })
  12192.             .replace(/\s+$/, function(pad) {
  12193.                 lineRange.end -= pad.length;
  12194.                 return '';
  12195.             });
  12196.         
  12197.         var css = require('cssResolver');
  12198.         var gradient = module.parse(line);
  12199.         if (gradient) {
  12200.             var props = getPropertiesForGradient(gradient, propertyName);
  12201.             props.push({
  12202.                 name: propertyName,
  12203.                 value: module.toString(gradient) + '${2}'
  12204.             });
  12205.             
  12206.             var sep = css.getSyntaxPreference('valueSeparator', syntax);
  12207.             var end = css.getSyntaxPreference('propertyEnd', syntax);
  12208.             props = _.map(props, function(item) {
  12209.                 return item.name + sep + item.value + end;
  12210.             });
  12211.             
  12212.             editor.replaceContent(props.join('\n'), lineRange.start, lineRange.end);
  12213.             return true;
  12214.         }
  12215.         
  12216.         return false;
  12217.     }
  12218.     
  12219.     /**
  12220.      * Search for gradient definition inside CSS value under cursor
  12221.      * @param {String} content
  12222.      * @param {Number} pos
  12223.      * @returns {Object}
  12224.      */
  12225.     function findGradientFromPosition(content, pos) {
  12226.         var cssProp = null;
  12227.         /** @type EditContainer */
  12228.         var cssRule = require('cssEditTree').parseFromPosition(content, pos, true);
  12229.         
  12230.         if (cssRule) {
  12231.             cssProp = cssRule.itemFromPosition(pos, true);
  12232.             if (!cssProp) {
  12233.                 // in case user just started writing CSS property
  12234.                 // and didn't include semicolonΓÇôtry another approach
  12235.                 cssProp = _.find(cssRule.list(), function(elem) {
  12236.                     return elem.range(true).end == pos;
  12237.                 });
  12238.             }
  12239.         }
  12240.         
  12241.         return {
  12242.             rule: cssRule,
  12243.             property: cssProp
  12244.         };
  12245.     }
  12246.     
  12247.     // XXX register expand abbreviation handler
  12248.     /**
  12249.      * @param {IEmmetEditor} editor
  12250.      * @param {String} syntax
  12251.      * @param {String} profile
  12252.      */
  12253.     require('expandAbbreviation').addHandler(function(editor, syntax, profile) {
  12254.         var info = require('editorUtils').outputInfo(editor, syntax, profile);
  12255.         if (!_.include(cssSyntaxes, info.syntax))
  12256.             return false;
  12257.         
  12258.         // let's see if we are expanding gradient definition
  12259.         var caret = editor.getCaretPos();
  12260.         var content = info.content;
  12261.         var css = findGradientFromPosition(content, caret);
  12262.         
  12263.         if (css.property) {
  12264.             // make sure that caret is inside property value with gradient 
  12265.             // definition
  12266.             var g = findGradient(css.property);
  12267.             if (g) {
  12268.                 var ruleStart = css.rule.options.offset || 0;
  12269.                 var ruleEnd = ruleStart + css.rule.toString().length;
  12270.                 
  12271.                 // Handle special case:
  12272.                 // user wrote gradient definition between existing CSS 
  12273.                 // properties and did not finished it with semicolon.
  12274.                 // In this case, we have semicolon right after gradient 
  12275.                 // definition and re-parse rule again
  12276.                 if (/[\n\r]/.test(css.property.value())) {
  12277.                     // insert semicolon at the end of gradient definition
  12278.                     var insertPos = css.property.valueRange(true).start + g.valueRange.end;
  12279.                     content = require('utils').replaceSubstring(content, ';', insertPos);
  12280.                     var newCss = findGradientFromPosition(content, caret);
  12281.                     if (newCss.property) {
  12282.                         g = findGradient(newCss.property);
  12283.                         css = newCss;
  12284.                     }
  12285.                 }
  12286.                 
  12287.                 // make sure current property has terminating semicolon
  12288.                 css.property.end(';');
  12289.                 
  12290.                 pasteGradient(css.property, g.gradient, g.valueRange);
  12291.                 editor.replaceContent(css.rule.toString(), ruleStart, ruleEnd, true);
  12292.                 return true;
  12293.             }
  12294.         }
  12295.         
  12296.         return expandGradientOutsideValue(editor, syntax);
  12297.     });
  12298.     
  12299.     // XXX register "Reflect CSS Value" action delegate
  12300.     /**
  12301.      * @param {EditElement} property
  12302.      */
  12303.     require('reflectCSSValue').addHandler(function(property) {
  12304.         var utils = require('utils');
  12305.         
  12306.         var g = findGradient(property);
  12307.         if (!g)
  12308.             return false;
  12309.         
  12310.         var value = property.value();
  12311.         var val = function(v) {
  12312.             return utils.replaceSubstring(value, v, g.valueRange);
  12313.         };
  12314.         
  12315.         // reflect value for properties with the same name
  12316.         _.each(property.parent.getAll(getPrefixedNames(property.name())), function(prop) {
  12317.             if (prop === property)
  12318.                 return;
  12319.             
  12320.             // check if property value starts with gradient definition
  12321.             var m = prop.value().match(/^\s*(\-([a-z]+)\-)?linear\-gradient/);
  12322.             if (m) {
  12323.                 prop.value(val(module.toString(g.gradient, m[2] || '')));
  12324.             } else if (m = prop.value().match(/\s*\-webkit\-gradient/)) {
  12325.                 // old webkit gradient definition
  12326.                 prop.value(val(module.oldWebkitLinearGradient(g.gradient)));
  12327.             }
  12328.         });
  12329.         
  12330.         return true;
  12331.     });
  12332.     
  12333.     return module = {
  12334.         /**
  12335.          * Parses gradient definition
  12336.          * @param {String} gradient
  12337.          * @returns {Object}
  12338.          */
  12339.         parse: function(gradient) {
  12340.             var result = null;
  12341.             require('utils').trim(gradient).replace(/^([\w\-]+)\((.+?)\)$/, function(str, type, definition) {
  12342.                 // remove vendor prefix
  12343.                 type = type.toLowerCase().replace(/^\-[a-z]+\-/, '');
  12344.                 if (type == 'linear-gradient' || type == 'lg') {
  12345.                     result = parseLinearGradient(definition);
  12346.                     return '';
  12347.                 }
  12348.                 
  12349.                 return str;
  12350.             });
  12351.             
  12352.             return result;
  12353.         },
  12354.         
  12355.         /**
  12356.          * Produces linear gradient definition used in early Webkit 
  12357.          * implementations
  12358.          * @param {Object} gradient Parsed gradient
  12359.          * @returns {String}
  12360.          */
  12361.         oldWebkitLinearGradient: function(gradient) {
  12362.             if (_.isString(gradient))
  12363.                 gradient = this.parse(gradient);
  12364.             
  12365.             if (!gradient)
  12366.                 return null;
  12367.             
  12368.             var colorStops = _.map(gradient.colorStops, _.clone);
  12369.             
  12370.             // normalize color-stops position
  12371.             _.each(colorStops, function(cs) {
  12372.                 if (!('position' in cs)) // implied position
  12373.                     return;
  12374.                 
  12375.                 if (~cs.position.indexOf('.') || cs.unit == '%') {
  12376.                     cs.position = parseFloat(cs.position) / (cs.unit == '%' ? 100 : 1);
  12377.                 } else {
  12378.                     throw "Can't convert color stop '" + (cs.position + (cs.unit || '')) + "'";
  12379.                 }
  12380.             });
  12381.             
  12382.             fillImpliedPositions(colorStops);
  12383.             
  12384.             // transform color-stops into string representation
  12385.             colorStops = _.map(colorStops, function(cs, i) {
  12386.                 if (!cs.position && !i)
  12387.                     return 'from(' + cs.color + ')';
  12388.                 
  12389.                 if (cs.position == 1 && i == colorStops.length - 1)
  12390.                     return 'to(' + cs.color + ')';
  12391.                 
  12392.                 return 'color-stop(' + (cs.position.toFixed(2).replace(/\.?0+$/, '')) + ', ' + cs.color + ')';
  12393.             });
  12394.             
  12395.             return '-webkit-gradient(linear, ' 
  12396.                 + oldWebkitDirection(gradient.direction)
  12397.                 + ', '
  12398.                 + colorStops.join(', ')
  12399.                 + ')';
  12400.         },
  12401.         
  12402.         /**
  12403.          * Returns string representation of parsed gradient
  12404.          * @param {Object} gradient Parsed gradient
  12405.          * @param {String} prefix Vendor prefix
  12406.          * @returns {String}
  12407.          */
  12408.         toString: function(gradient, prefix) {
  12409.             if (gradient.type == 'linear') {
  12410.                 var fn = (prefix ? '-' + prefix + '-' : '') + 'linear-gradient';
  12411.                 
  12412.                 // transform color-stops
  12413.                 var colorStops = _.map(gradient.colorStops, function(cs) {
  12414.                     return cs.color + ('position' in cs 
  12415.                             ? ' ' + cs.position + (cs.unit || '')
  12416.                             : '');
  12417.                 });
  12418.                 
  12419.                 if (gradient.direction 
  12420.                         && (!prefs.get('css.gradient.omitDefaultDirection') 
  12421.                         || !_.include(defaultLinearDirections, gradient.direction))) {
  12422.                     colorStops.unshift(gradient.direction);
  12423.                 }
  12424.                 
  12425.                 return fn + '(' + colorStops.join(', ') + ')';
  12426.             }
  12427.         }
  12428.     };
  12429. });/**
  12430.  * Module adds support for generators: a regexp-based abbreviation resolver 
  12431.  * that can produce custom output.
  12432.  * @param {Function} require
  12433.  * @param {Underscore} _
  12434.  */
  12435. emmet.exec(function(require, _) {
  12436.     /** @type HandlerList */
  12437.     var generators = require('handlerList').create();
  12438.     var resources = require('resources');
  12439.     
  12440.     _.extend(resources, {
  12441.         /**
  12442.          * Add generator. A generator function <code>fn</code> will be called 
  12443.          * only if current abbreviation matches <code>regexp</code> regular 
  12444.          * expression and this function should return <code>null</code> if
  12445.          * abbreviation cannot be resolved
  12446.          * @param {RegExp} regexp Regular expression for abbreviation element name
  12447.          * @param {Function} fn Resolver function
  12448.          * @param {Object} options Options list as described in 
  12449.          * {@link HandlerList#add()} method
  12450.          */
  12451.         addGenerator: function(regexp, fn, options) {
  12452.             if (_.isString(regexp))
  12453.                 regexp = new RegExp(regexp);
  12454.             
  12455.             generators.add(function(node, syntax) {
  12456.                 var m;
  12457.                 if ((m = regexp.exec(node.name()))) {
  12458.                     return fn(m, node, syntax);
  12459.                 }
  12460.                 
  12461.                 return null;
  12462.             }, options);
  12463.         }
  12464.     });
  12465.     
  12466.     resources.addResolver(function(node, syntax) {
  12467.         return generators.exec(null, _.toArray(arguments));
  12468.     });
  12469. });/**
  12470.  * Module for resolving tag names: returns best matched tag name for child
  12471.  * element based on passed parent's tag name. Also provides utility function
  12472.  * for element type detection (inline, block-level, empty)
  12473.  * @param {Function} require
  12474.  * @param {Underscore} _
  12475.  */
  12476. emmet.define('tagName', function(require, _) {
  12477.     var elementTypes = {
  12478.         empty: 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,keygen,command'.split(','),
  12479.         blockLevel: 'address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,link,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul,h1,h2,h3,h4,h5,h6'.split(','),
  12480.         inlineLevel: 'a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'.split(',')
  12481.     };
  12482.     
  12483.     var elementMap = {
  12484.         'p': 'span',
  12485.         'ul': 'li',
  12486.         'ol': 'li',
  12487.         'table': 'tr',
  12488.         'tr': 'td',
  12489.         'tbody': 'tr',
  12490.         'thead': 'tr',
  12491.         'tfoot': 'tr',
  12492.         'colgroup': 'col',
  12493.         'select': 'option',
  12494.         'optgroup': 'option',
  12495.         'audio': 'source',
  12496.         'video': 'source',
  12497.         'object': 'param',
  12498.         'map': 'area'
  12499.     };
  12500.     
  12501.     return {
  12502.         /**
  12503.          * Returns best matched child element name for passed parent's
  12504.          * tag name
  12505.          * @param {String} name
  12506.          * @returns {String}
  12507.          * @memberOf tagName
  12508.          */
  12509.         resolve: function(name) {
  12510.             name = (name || '').toLowerCase();
  12511.             
  12512.             if (name in elementMap)
  12513.                 return this.getMapping(name);
  12514.             
  12515.             if (this.isInlineLevel(name))
  12516.                 return 'span';
  12517.             
  12518.             return 'div';
  12519.         },
  12520.         
  12521.         /**
  12522.          * Returns mapped child element name for passed parent's name 
  12523.          * @param {String} name
  12524.          * @returns {String}
  12525.          */
  12526.         getMapping: function(name) {
  12527.             return elementMap[name.toLowerCase()];
  12528.         },
  12529.         
  12530.         /**
  12531.          * Check if passed element name belongs to inline-level element
  12532.          * @param {String} name
  12533.          * @returns {Boolean}
  12534.          */
  12535.         isInlineLevel: function(name) {
  12536.             return this.isTypeOf(name, 'inlineLevel');
  12537.         },
  12538.         
  12539.         /**
  12540.          * Check if passed element belongs to block-level element.
  12541.          * For better matching of unknown elements (for XML, for example), 
  12542.          * you should use <code>!this.isInlineLevel(name)</code>
  12543.          * @returns {Boolean}
  12544.          */
  12545.         isBlockLevel: function(name) {
  12546.             return this.isTypeOf(name, 'blockLevel');
  12547.         },
  12548.         
  12549.         /**
  12550.          * Check if passed element is void (i.e. should not have closing tag).
  12551.          * @returns {Boolean}
  12552.          */
  12553.         isEmptyElement: function(name) {
  12554.             return this.isTypeOf(name, 'empty');
  12555.         },
  12556.         
  12557.         /**
  12558.          * Generic function for testing if element name belongs to specified
  12559.          * elements collection
  12560.          * @param {String} name Element name
  12561.          * @param {String} type Collection name
  12562.          * @returns {Boolean}
  12563.          */
  12564.         isTypeOf: function(name, type) {
  12565.             return _.include(elementTypes[type], name);
  12566.         },
  12567.         
  12568.         /**
  12569.          * Adds new parentΓÇôchild mapping
  12570.          * @param {String} parent
  12571.          * @param {String} child
  12572.          */
  12573.         addMapping: function(parent, child) {
  12574.             elementMap[parent] = child;
  12575.         },
  12576.         
  12577.         /**
  12578.          * Removes parent-child mapping
  12579.          */
  12580.         removeMapping: function(parent) {
  12581.             if (parent in elementMap)
  12582.                 delete elementMap[parent];
  12583.         },
  12584.         
  12585.         /**
  12586.          * Adds new element into collection
  12587.          * @param {String} name Element name
  12588.          * @param {String} collection Collection name
  12589.          */
  12590.         addElementToCollection: function(name, collection) {
  12591.             if (!elementTypes[collection])
  12592.                 elementTypes[collection] = [];
  12593.             
  12594.             var col = this.getCollection(collection);
  12595.             if (!_.include(col, name))
  12596.                 col.push(name);
  12597.         },
  12598.         
  12599.         /**
  12600.          * Removes element name from specified collection
  12601.          * @param {String} name Element name
  12602.          * @param {String} collection Collection name
  12603.          * @returns
  12604.          */
  12605.         removeElementFromCollection: function(name, collection) {
  12606.             if (collection in elementTypes) {
  12607.                 elementTypes[collection] = _.without(this.getCollection(collection), name);
  12608.             }
  12609.         },
  12610.         
  12611.         /**
  12612.          * Returns elements name collection
  12613.          * @param {String} name Collection name
  12614.          * @returns {Array}
  12615.          */
  12616.         getCollection: function(name) {
  12617.             return elementTypes[name];
  12618.         }
  12619.     };
  12620. });/**
  12621.  * A back-end bootstrap module with commonly used methods for loading user data
  12622.  * @param {Function} require
  12623.  * @param {Underscore} _  
  12624.  */
  12625. emmet.define('bootstrap', function(require, _) {
  12626.     
  12627.     /**
  12628.      * Returns file name part from path
  12629.      * @param {String} path Path to file
  12630.      * @return {String}
  12631.      */
  12632.     function getFileName(path) {
  12633.         var re = /([\w\.\-]+)$/i;
  12634.         var m = re.exec(path);
  12635.         return m ? m[1] : '';
  12636.     }
  12637.     
  12638.     /**
  12639.      * Returns base path (path to folder of file)
  12640.      * @param {String} path Path to file
  12641.      * @return {String}
  12642.      */
  12643.     function getBasePath(path) {
  12644.         return path.substring(0, path.length - getFileName(path).length);
  12645.     }
  12646.  
  12647.     /**
  12648.      * Normalizes profile definition: converts some
  12649.      * properties to valid data types
  12650.      * @param {Object} profile
  12651.      * @return {Object}
  12652.      */
  12653.     function normalizeProfile(profile) {
  12654.         if (_.isObject(profile)) {
  12655.             if ('indent' in profile) {
  12656.                 profile.indent = !!profile.indent;
  12657.             }
  12658.  
  12659.             if ('self_closing_tag' in profile) {
  12660.                 if (_.isNumber(profile.self_closing_tag)) {
  12661.                     profile.self_closing_tag = !!profile.self_closing_tag;
  12662.                 }
  12663.             }
  12664.         }
  12665.  
  12666.         return profile;
  12667.     }
  12668.     
  12669.     return {
  12670.         /**
  12671.          * Loads Emmet extensions. Extensions are simple .js files that
  12672.          * uses Emmet modules and resources to create new actions, modify
  12673.          * existing ones etc.
  12674.          * @param {Array} fileList List of absolute paths to files in extensions 
  12675.          * folder. Back-end app should not filter this list (e.g. by extension) 
  12676.          * but return it "as-is" so bootstrap can decide how to load contents 
  12677.          * of each file.
  12678.          * This method requires a <code>file</code> module of <code>IEmmetFile</code> 
  12679.          * interface to be implemented.
  12680.          * @memberOf bootstrap
  12681.          */
  12682.         loadExtensions: function(fileList) {
  12683.             var file = require('file');
  12684.             var payload = {};
  12685.             var utils = require('utils');
  12686.             var userSnippets = null;
  12687.             
  12688.             _.each(fileList, function(f) {
  12689.                 switch (file.getExt(f)) {
  12690.                     case 'js':
  12691.                         try {
  12692.                             eval(file.read(f));
  12693.                         } catch (e) {
  12694.                             emmet.log('Unable to eval "' + f + '" file: '+ e);
  12695.                         }
  12696.                         break;
  12697.                     case 'json':
  12698.                         var fileName = getFileName(f).toLowerCase().replace(/\.json$/, '');
  12699.                         if (/^snippets/.test(fileName)) {
  12700.                             if (fileName === 'snippets') {
  12701.                                 // data in snippets.json is more important to user
  12702.                                 userSnippets = this.parseJSON(file.read(f));
  12703.                             } else {
  12704.                                 payload.snippets = utils.deepMerge(payload.snippets || {}, this.parseJSON(file.read(f)));
  12705.                             }
  12706.                         } else {
  12707.                             payload[fileName] = file.read(f);
  12708.                         }
  12709.                         
  12710.                         break;
  12711.                 }
  12712.             }, this);
  12713.             
  12714.             if (userSnippets) {
  12715.                 payload.snippets = utils.deepMerge(payload.snippets || {}, userSnippets);
  12716.             }
  12717.             
  12718.             this.loadUserData(payload);
  12719.         },
  12720.         
  12721.         /**
  12722.          * Loads preferences from JSON object (or string representation of JSON)
  12723.          * @param {Object} data
  12724.          * @returns
  12725.          */
  12726.         loadPreferences: function(data) {
  12727.             require('preferences').load(this.parseJSON(data));
  12728.         },
  12729.         
  12730.         /**
  12731.          * Loads user snippets and abbreviations. It doesnΓÇÖt replace current
  12732.          * user resource vocabulary but merges it with passed one. If you need 
  12733.          * to <i>replaces</i> user snippets you should call 
  12734.          * <code>resetSnippets()</code> method first
  12735.          */
  12736.         loadSnippets: function(data) {
  12737.             data = this.parseJSON(data);
  12738.             
  12739.             var res = require('resources');
  12740.             var userData = res.getVocabulary('user') || {};
  12741.             res.setVocabulary(require('utils').deepMerge(userData, data), 'user');
  12742.         },
  12743.         
  12744.         /**
  12745.          * Helper function that loads default snippets, defined in projectΓÇÖs
  12746.          * <i>snippets.json</i>
  12747.          * @param {Object} data
  12748.          */
  12749.         loadSystemSnippets: function(data) {
  12750.             require('resources').setVocabulary(this.parseJSON(data), 'system');
  12751.         },
  12752.         
  12753.         /**
  12754.          * Removes all user-defined snippets
  12755.          */
  12756.         resetSnippets: function() {
  12757.             require('resources').setVocabulary({}, 'user');
  12758.         },
  12759.         
  12760.         /**
  12761.          * Helper function that loads all user data (snippets and preferences)
  12762.          * defined as a single JSON object. This is useful for loading data 
  12763.          * stored in a common storage, for example <code>NSUserDefaults</code>
  12764.          * @param {Object} data
  12765.          */
  12766.         loadUserData: function(data) {
  12767.             data = this.parseJSON(data);
  12768.             if (data.snippets) {
  12769.                 this.loadSnippets(data.snippets);
  12770.             }
  12771.             
  12772.             if (data.preferences) {
  12773.                 this.loadPreferences(data.preferences);
  12774.             }
  12775.             
  12776.             if (data.profiles) {
  12777.                 this.loadProfiles(data.profiles);
  12778.             }
  12779.             
  12780.             var profiles = data.syntaxProfiles || data.syntaxprofiles;
  12781.             if (profiles) {
  12782.                 this.loadSyntaxProfiles(profiles);
  12783.             }
  12784.         },
  12785.         
  12786.         /**
  12787.          * Resets all user-defined data: preferences, snippets etc.
  12788.          * @returns
  12789.          */
  12790.         resetUserData: function() {
  12791.             this.resetSnippets();
  12792.             require('preferences').reset();
  12793.             require('profile').reset();
  12794.         },
  12795.         
  12796.         /**
  12797.          * Load syntax-specific output profiles. These are essentially 
  12798.          * an extension to syntax snippets 
  12799.          * @param {Object} profiles Dictionary of profiles
  12800.          */
  12801.         loadSyntaxProfiles: function(profiles) {
  12802.             profiles = this.parseJSON(profiles);
  12803.             var snippets = {};
  12804.             _.each(profiles, function(options, syntax) {
  12805.                 if (!(syntax in snippets)) {
  12806.                     snippets[syntax] = {};
  12807.                 }
  12808.                 snippets[syntax].profile = normalizeProfile(options);
  12809.             });
  12810.             
  12811.             this.loadSnippets(snippets);
  12812.         },
  12813.         
  12814.         /**
  12815.          * Load named profiles
  12816.          * @param {Object} profiles
  12817.          */
  12818.         loadProfiles: function(profiles) {
  12819.             var profile = require('profile');
  12820.             _.each(this.parseJSON(profiles), function(options, name) {
  12821.                 profile.create(name, normalizeProfile(options));
  12822.             });
  12823.         },
  12824.         
  12825.         /**
  12826.          * Dead simple string-to-JSON parser
  12827.          * @param {String} str
  12828.          * @returns {Object}
  12829.          */
  12830.         parseJSON: function(str) {
  12831.             if (_.isObject(str))
  12832.                 return str;
  12833.             
  12834.             try {
  12835.                 return (new Function('return ' + str))();
  12836.             } catch(e) {
  12837.                 return {};
  12838.             }
  12839.         }
  12840.     };
  12841. });/**
  12842.  * Implementation of @{link IEmmetFile} interface
  12843.  * See `javascript/interfaces/IEmmetFile.js`
  12844.  * @param {Function} require
  12845.  * @param {Underscore} _
  12846.  * @constructor
  12847.  */
  12848. emmet.define('file', function(require, _) {
  12849.     var backward = {
  12850.         'C7': '80',
  12851.         'FC': '81',
  12852.         'E9': '82',
  12853.         'E2': '83',
  12854.         'E4': '84',
  12855.         'E0': '85',
  12856.         'E5': '86',
  12857.         'E7': '87',
  12858.         'EA': '88',
  12859.         'EB': '89',
  12860.         'E8': '8A',
  12861.         'EF': '8B',
  12862.         'EE': '8C',
  12863.         'EC': '8D',
  12864.         'C4': '8E',
  12865.         'C5': '8F',
  12866.         'C9': '90',
  12867.         'E6': '91',
  12868.         'C6': '92',
  12869.         'F4': '93',
  12870.         'F6': '94',
  12871.         'F2': '95',
  12872.         'FB': '96',
  12873.         'F9': '97',
  12874.         'FF': '98',
  12875.         'D6': '99',
  12876.         'DC': '9A',
  12877.         'A2': '9B',
  12878.         'A3': '9C',
  12879.         'A5': '9D',
  12880.         '20A7': '9E',
  12881.         '192': '9F',
  12882.         'E1': 'A0',
  12883.         'ED': 'A1',
  12884.         'F3': 'A2',
  12885.         'FA': 'A3',
  12886.         'F1': 'A4',
  12887.         'D1': 'A5',
  12888.         'AA': 'A6',
  12889.         'BA': 'A7',
  12890.         'BF': 'A8',
  12891.         '2310': 'A9',
  12892.         'AC': 'AA',
  12893.         'BD': 'AB',
  12894.         'BC': 'AC',
  12895.         'A1': 'AD',
  12896.         'AB': 'AE',
  12897.         'BB': 'AF',
  12898.         '2591': 'B0',
  12899.         '2592': 'B1',
  12900.         '2593': 'B2',
  12901.         '2502': 'B3',
  12902.         '2524': 'B4',
  12903.         '2561': 'B5',
  12904.         '2562': 'B6',
  12905.         '2556': 'B7',
  12906.         '2555': 'B8',
  12907.         '2563': 'B9',
  12908.         '2551': 'BA',
  12909.         '2557': 'BB',
  12910.         '255D': 'BC',
  12911.         '255C': 'BD',
  12912.         '255B': 'BE',
  12913.         '2510': 'BF',
  12914.         '2514': 'C0',
  12915.         '2534': 'C1',
  12916.         '252C': 'C2',
  12917.         '251C': 'C3',
  12918.         '2500': 'C4',
  12919.         '253C': 'C5',
  12920.         '255E': 'C6',
  12921.         '255F': 'C7',
  12922.         '255A': 'C8',
  12923.         '2554': 'C9',
  12924.         '2569': 'CA',
  12925.         '2566': 'CB',
  12926.         '2560': 'CC',
  12927.         '2550': 'CD',
  12928.         '256C': 'CE',
  12929.         '2567': 'CF',
  12930.         '2568': 'D0',
  12931.         '2564': 'D1',
  12932.         '2565': 'D2',
  12933.         '2559': 'D3',
  12934.         '2558': 'D4',
  12935.         '2552': 'D5',
  12936.         '2553': 'D6',
  12937.         '256B': 'D7',
  12938.         '256A': 'D8',
  12939.         '2518': 'D9',
  12940.         '250C': 'DA',
  12941.         '2588': 'DB',
  12942.         '2584': 'DC',
  12943.         '258C': 'DD',
  12944.         '2590': 'DE',
  12945.         '2580': 'DF',
  12946.         '3B1': 'E0',
  12947.         'DF': 'E1',
  12948.         '393': 'E2',
  12949.         '3C0': 'E3',
  12950.         '3A3': 'E4',
  12951.         '3C3': 'E5',
  12952.         'B5': 'E6',
  12953.         '3C4': 'E7',
  12954.         '3A6': 'E8',
  12955.         '398': 'E9',
  12956.         '3A9': 'EA',
  12957.         '3B4': 'EB',
  12958.         '221E': 'EC',
  12959.         '3C6': 'ED',
  12960.         '3B5': 'EE',
  12961.         '2229': 'EF',
  12962.         '2261': 'F0',
  12963.         'B1': 'F1',
  12964.         '2265': 'F2',
  12965.         '2264': 'F3',
  12966.         '2320': 'F4',
  12967.         '2321': 'F5',
  12968.         'F7': 'F6',
  12969.         '2248': 'F7',
  12970.         'B0': 'F8',
  12971.         '2219': 'F9',
  12972.         'B7': 'FA',
  12973.         '221A': 'FB',
  12974.         '207F': 'FC',
  12975.         'B2': 'FD',
  12976.         '25A0': 'FE',
  12977.         'A0': 'FF'
  12978.     };
  12979.     
  12980.     var hD = "0123456789ABCDEF";
  12981.  
  12982.     function d2h(d) {
  12983.         var h = hD.substr(d & 15, 1);
  12984.         while (d > 15) {
  12985.             d >>>= 4;
  12986.             h = hD.substr(d & 15, 1) + h;
  12987.         }
  12988.         
  12989.         return h;
  12990.     }
  12991.  
  12992.     function h2d(h) {
  12993.         return parseInt(h, 16);
  12994.     }
  12995.  
  12996.     function _readFile(path) {
  12997.         var bs = new ActiveXObject("ADODB.Stream");
  12998.         bs.Type = 2;
  12999.         bs.CharSet = '437';
  13000.         bs.Open();
  13001.         bs.LoadFromFile(path);
  13002.         
  13003.         var what = bs.ReadText;
  13004.         bs.Close();
  13005.         return what;
  13006.     }
  13007.  
  13008.     return {
  13009.         read: function(path) {
  13010.             var content = _readFile(path);
  13011.             
  13012.             // encode result
  13013.             var encArray = [];
  13014.             for (var i = 0, sL = content.length; i < sL; i++) {
  13015.                 var cc = content.charCodeAt(i);
  13016.                 if (cc >= 128) {
  13017.                     cc = h2d(backward['' + d2h(cc)]);
  13018.                 }
  13019.                 encArray.push(String.fromCharCode(cc));
  13020.             }
  13021.             
  13022.             return encArray.join('') || '';
  13023.         },
  13024.         
  13025.         locateFile: function(baseFile, fileName) {
  13026.             var fso = new ActiveXObject("Scripting.FileSystemObject");
  13027.             var parent = baseFile, tmp;
  13028.             
  13029.             while (parent = fso.GetParentFolderName(parent)) {
  13030.                 tmp = this.createPath(parent, fileName);
  13031.                 if (fso.FileExists(tmp)) {
  13032.                     return fso.GetAbsolutePathName(tmp);
  13033.                 }
  13034.             }
  13035.             
  13036.             return '';
  13037.         },
  13038.         
  13039.         createPath: function(parent, fileName) {
  13040.             var fso = new ActiveXObject("Scripting.FileSystemObject");
  13041.             return fso.BuildPath(parent, fileName);
  13042.         },
  13043.         
  13044.         save: function(file, content) {
  13045.             var fso = new ActiveXObject("Scripting.FileSystemObject");
  13046.             var f = null;
  13047.             
  13048.             try {
  13049.                 f = fso.OpenTextFile(file, 2, true, 0);
  13050.                 f.Write(content);
  13051.             } catch(e){
  13052.                 debug(e);
  13053.             }
  13054.             
  13055.             if (f)
  13056.                 f.Close();
  13057.         },
  13058.         
  13059.         getExt: function(file) {
  13060.             var m = (file || '').match(/\.([\w\-]+)$/);
  13061.             return m ? m[1].toLowerCase() : '';
  13062.         }
  13063.     };
  13064. });/**
  13065.  * @param {Function} require
  13066.  * @param {Underscore} _
  13067.  * @constructor
  13068.  */
  13069. emmet.define('editorProxy', function(require, _) {
  13070.     function getView() {
  13071.         var view = epp.currentView;
  13072.         var res = require('resources');
  13073.         var utils = require('utils');
  13074.         
  13075.         var indentation = res.getVariable('indentation');
  13076.         if (view.useTabs) {
  13077.             indentation = '\t';
  13078.         } else {
  13079.             indentation = utils.repeatString(' ', view.tabSize);
  13080.         }
  13081.         
  13082.         res.setVariable('indentation', indentation);
  13083.         utils.setNewline('\r\n');
  13084.         
  13085.         return view;
  13086.     }
  13087.     
  13088.     return {
  13089.         /** @memberOf editorProxy */
  13090.         getSelectionRange: function() {
  13091.             var view = getView();
  13092.             return {
  13093.                 start: view.anchor,
  13094.                 end: view.pos
  13095.             };
  13096.         },
  13097.  
  13098.         createSelection: function(start, end) {
  13099.             getView().setSel(start, end);
  13100.         },
  13101.  
  13102.         getCurrentLineRange: function() {
  13103.             var view = getView();
  13104.             var lineNumber = view.lineNumber;
  13105.             var lineStart = view.lineIndex(lineNumber);
  13106.             var lineEnd = lineStart + view.lineLength(lineNumber);
  13107.  
  13108.             return {
  13109.                 start: lineStart, 
  13110.                 end: lineEnd
  13111.             };
  13112.         },
  13113.  
  13114.         getCaretPos: function() {
  13115.             return getView().pos;
  13116.         },
  13117.  
  13118.         setCaretPos: function(pos) {
  13119.             this.createSelection(pos, pos);
  13120.         },
  13121.  
  13122.         getCurrentLine: function() {
  13123.             return getView().line;
  13124.         },
  13125.  
  13126.         replaceContent: function(value, start, end, noIndent) {
  13127.             if (_.isUndefined(end)) 
  13128.                 end = _.isUndefined(start) ? content.length : start;
  13129.             if (_.isUndefined(start)) start = 0;
  13130.             var utils = require('utils');
  13131.             
  13132.             // indent new value
  13133.             if (!noIndent) {
  13134.                 value = utils.padString(value, utils.getLinePadding(this.getCurrentLine()));
  13135.             }
  13136.             
  13137.             // find new caret position
  13138.             var tabstopData = require('tabStops').extract(value, {
  13139.                 escape: function(ch) {
  13140.                     return ch;
  13141.                 }
  13142.             });
  13143.             value = tabstopData.text;
  13144.             var firstTabStop = tabstopData.tabstops[0];
  13145.             
  13146.             if (firstTabStop) {
  13147.                 firstTabStop.start += start;
  13148.                 firstTabStop.end += start;
  13149.             } else {
  13150.                 firstTabStop = {
  13151.                     start: value.length + start,
  13152.                     end: value.length + start
  13153.                 };
  13154.             }
  13155.             
  13156.             // insert new text
  13157.             this.createSelection(start, end);
  13158.             getView().replaceSel(value, 1);
  13159.             this.createSelection(firstTabStop.start, firstTabStop.end);
  13160.         },
  13161.  
  13162.         getContent: function(){
  13163.             return getView().text || '';
  13164.         },
  13165.  
  13166.         getSyntax: function() {
  13167.             var view = getView();
  13168.             var syntax = (view.syntaxScope || '').toLowerCase();
  13169.             
  13170.             if (syntax == 'xml' && /\.xslt?(\.|$)/i.test(this.getFilePath()))
  13171.                 syntax = 'xsl';
  13172.             
  13173.             return require('actionUtils').detectSyntax(this, syntax);
  13174.         },
  13175.  
  13176.         getProfileName: function() {
  13177.             return require('actionUtils').detectProfile(this);
  13178.         },
  13179.  
  13180.         prompt: function(title, value) {
  13181.             //var ie = new ActiveXObject("InternetExplorer.Application");
  13182.             //ie.navigate('about:blank'); 
  13183.             //ie.Visible = 0;
  13184.             
  13185.             //while (ie.Busy) {}
  13186.             //var obj = ie.Document.Script;
  13187.             //var input = obj.prompt(title, value || '');
  13188.             //ie.Quit();
  13189.             //return input;
  13190.             return getView().prompt(title);
  13191.         },
  13192.  
  13193.         getSelection: function() {
  13194.             return getView().selection || '';
  13195.         },
  13196.  
  13197.         getFilePath: function() {
  13198.             return getView().file;
  13199.         }
  13200.     };
  13201. });/**
  13202.  * @constructor
  13203.  * @param {Function} require
  13204.  * @param {Underscore} _
  13205.  */
  13206. emmet.exec(function(require, _) {
  13207.     var file = require('file');
  13208.     
  13209.     // path to Emmet extensions and user's custom snippets 
  13210.     var extensionsDir = file.createPath(epp.stxDir, 'emmet-extensions');
  13211.     
  13212.     var bootstrap = require('bootstrap');
  13213.     
  13214.     // load snippets
  13215.     bootstrap.loadSystemSnippets(file.read(file.createPath(epp.stxDir, 'snippets.json')));
  13216.     
  13217.     // load extensions
  13218.     var fso = new ActiveXObject("Scripting.FileSystemObject");
  13219.     if (fso.FolderExists(extensionsDir)) {
  13220.         var fileList = [];
  13221.         var extensionFiles = fso.GetFolder(extensionsDir).Files;
  13222.         
  13223.         var fileEnum = new Enumerator(extensionFiles);
  13224.         for (; !fileEnum.atEnd(); fileEnum.moveNext()) {
  13225.             fileList.push(fileEnum.item().Path);
  13226.         }
  13227.         
  13228.         bootstrap.loadExtensions(fileList);
  13229.     }
  13230.     
  13231.     //function addMenuItems(menu, items) {
  13232.         //_.each(items, function(item) {
  13233.             //if (item.type == 'action') {
  13234.                 //var cfg = {
  13235.                     //text: item.label,
  13236.                     //cmd: function() {
  13237.                         //return require('actions').run(item.name, require('editorProxy'));
  13238.                     //}
  13239.                 //};
  13240.                 
  13241.                 //if (item.name in keymap) {
  13242.                     //System.addHotkey(setupHotkey(cfg, keymap[item.name]));
  13243.                     //cfg.text += '\t' + keymap[item.name];
  13244.                 //}
  13245.                 
  13246.                 //menu.addItem(cfg);
  13247.                 
  13248.             //} else if (item.type == 'submenu') {
  13249.                 //var submenu = menu.addMenu(item.name);
  13250.                 //addMenuItems(submenu, item.items);
  13251.             //}
  13252.         //});
  13253.     //}
  13254.     
  13255.     //// create menu items
  13256.     //var rootMenu = Editor.addMenu("Emmet");
  13257.     //addMenuItems(rootMenu, require('actions').getMenu());
  13258. });
  13259.  
  13260. var zen_controller = {
  13261.     runAction: function(action_name) {
  13262.         emmet.require('actions').run(action_name, emmet.require('editorProxy'));
  13263.     },
  13264.  
  13265.     hasSyntax: function() {
  13266.         var view = epp.currentView;
  13267.         var syntax = (view.syntaxScope || '').toLowerCase();
  13268.         return emmet.require('resources').hasSyntax(syntax);
  13269.     }
  13270. }
  13271.